├── .editorconfig ├── .gitattributes ├── .github ├── renovate.json └── workflows │ ├── publish.yml │ └── renovate-hugo-modules.yml ├── .gitignore ├── .husky └── pre-commit ├── .markdownlintignore ├── .prettierignore ├── .prettierrc.json ├── .stylelintignore ├── .stylelintrc.json ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── archetypes └── default.md ├── assets ├── css │ ├── critical │ │ ├── 00-vendor.css │ │ ├── 10-custom-media.css │ │ ├── 15-colors.css │ │ ├── 20-base.css │ │ ├── 20-scrollbar.css │ │ ├── 25-layout.css │ │ ├── 30-header.css │ │ ├── 35-sidebar.css │ │ ├── 40-post.css │ │ ├── 45-json-resume.css │ │ └── 50-social-share.css │ └── non-critical │ │ ├── 00-vendor.css │ │ ├── 10-custom-media.css │ │ ├── 15-footer.css │ │ └── 20-pagination.css └── js │ ├── dark-mode.js │ ├── flexsearch.js │ └── prism.js ├── config ├── _default │ ├── hugo.toml │ ├── module.toml │ └── taxonomies.toml └── production │ └── hugo.toml ├── content ├── about.md ├── blog │ ├── embed-video-files │ │ ├── index.md │ │ ├── my-video.jpg │ │ ├── my-video.mp4 │ │ └── my-video.webm │ ├── emoji-support.md │ ├── image-optimization │ │ ├── aaron-thomas-dMqlE7lgyOU-unsplash.jpg │ │ ├── alexandre-van-thuan-mr9FouttLGY-unsplash.jpg │ │ ├── index.md │ │ ├── lazy-loading.jpg │ │ ├── lazy-loading.mp4 │ │ └── lazy-loading.webm │ ├── markdown-syntax.md │ ├── math-typesetting.md │ ├── placeholder-text.md │ ├── prism-code-highlighting-showcase.md │ └── rich-content.md ├── cv.de.md └── cv.md ├── data └── json_resume │ └── en.json ├── eslint.config.mjs ├── go.mod ├── go.sum ├── hugo-theme-gruvbox-inkscape.svg ├── hugo-theme-gruvbox.svg ├── i18n └── .gitkeep ├── images ├── screenshot.png └── tn.png ├── layouts ├── 404.html ├── _default │ ├── _markup │ │ ├── render-heading.html │ │ ├── render-image.html │ │ └── render-link.html │ ├── baseof.html │ ├── list.html │ └── single.html ├── cv │ └── single.html ├── home.searchindex.json ├── index.html ├── partials │ ├── comments.html │ ├── footer.html │ ├── footer_end.html │ ├── head │ │ ├── dark-mode.html │ │ ├── favicons.html │ │ ├── head.html │ │ ├── head_end.html │ │ ├── head_start.html │ │ ├── resource-hints.html │ │ ├── seo.html │ │ └── stylesheets.html │ ├── header.html │ ├── icons │ │ ├── simple-icon.html │ │ ├── svg.html │ │ └── tabler-icon.html │ ├── image.html │ ├── pagination.html │ ├── post-list.html │ ├── post-meta.html │ ├── sidebar.html │ ├── social-share.html │ └── tag-cloud.html ├── robots.txt └── shortcodes │ ├── cv.html │ ├── prism-features.html │ └── video.html ├── package-lock.json ├── package.hugo.json ├── package.json ├── postcss.config.js ├── static ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── browserconfig.xml ├── css │ └── .gitkeep ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── js │ └── .gitkeep ├── mstile-144x144.png ├── mstile-150x150.png ├── mstile-310x150.png ├── mstile-310x310.png ├── mstile-70x70.png ├── richard-hendricks.webp ├── safari-pinned-tab.svg └── site.webmanifest └── theme.toml /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base"], 4 | "rebaseWhen": "behind-base-branch", 5 | "npm": { 6 | "fileMatch": ["(^|/)package\\.hugo\\.json$"], 7 | "rangeStrategy": "bump", 8 | "updateLockFiles": false, 9 | "ignoreTests": true, 10 | "ignorePaths": [ 11 | "package.json", 12 | 13 | "**/node_modules/**", 14 | "**/bower_components/**", 15 | "**/vendor/**", 16 | "**/examples/**", 17 | "**/__tests__/**", 18 | "**/test/**", 19 | "**/tests/**", 20 | "**/__fixtures__/**" 21 | ] 22 | }, 23 | "gomod": { 24 | "fileMatch": ["(^|/)go\\.mod$"], 25 | "rangeStrategy": "bump" 26 | }, 27 | "packageRules": [ 28 | { 29 | "matchManagers": ["gomod"], 30 | "matchDepTypes": ["indirect"], 31 | "enabled": true, 32 | "groupName": "Hugo Modules" 33 | }, 34 | { 35 | "extends": "packages:linters", 36 | "groupName": "linters" 37 | }, 38 | { 39 | "extends": "packages:postcss", 40 | "groupName": "postcss packages" 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Hugo Site 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | update_hugo_npm_dependencies: 10 | name: Update Hugo npm Dependencies 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | outputs: 15 | commit_hash: ${{ steps.commit_changes.outputs.commit_hash }} 16 | steps: 17 | - name: Checkout Repository 18 | uses: actions/checkout@v4 19 | 20 | - name: Install Node 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: "20" 24 | 25 | - name: Install Hugo 26 | uses: peaceiris/actions-hugo@v3 27 | with: 28 | hugo-version: "0.133.0" 29 | extended: true 30 | 31 | - name: Write composite package.json 32 | run: hugo mod npm pack 33 | 34 | - name: Install npm Packages 35 | run: npm install 36 | 37 | - name: Display Changes 38 | run: git status 39 | 40 | - name: Commit Changes 41 | id: commit_changes 42 | uses: stefanzweifel/git-auto-commit-action@v5 43 | with: 44 | commit_message: Update Hugo npm Dependencies 45 | commit_author: 46 | renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 47 | 48 | publish: 49 | name: Publish Hugo Site 50 | needs: update_hugo_npm_dependencies 51 | runs-on: ubuntu-latest 52 | permissions: 53 | contents: read 54 | deployments: write 55 | steps: 56 | - name: Checkout Repository 57 | uses: actions/checkout@v4 58 | with: 59 | fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod 60 | ref: ${{ needs.update_hugo_npm_dependencies.outputs.commit_hash }} 61 | 62 | - name: Install Node 63 | uses: actions/setup-node@v4 64 | with: 65 | node-version: "20" 66 | 67 | - name: Install Hugo 68 | uses: peaceiris/actions-hugo@v3 69 | with: 70 | hugo-version: "0.133.0" 71 | extended: true 72 | 73 | - name: Install npm Packages 74 | run: npm ci 75 | 76 | - name: Build Hugo 77 | run: hugo --minify 78 | 79 | - name: Deploy to Cloudflare Pages 80 | uses: cloudflare/pages-action@v1 81 | with: 82 | apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} 83 | accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} 84 | projectName: hugo-theme-gruvbox 85 | directory: ./public 86 | gitHubToken: ${{ secrets.GITHUB_TOKEN }} 87 | -------------------------------------------------------------------------------- /.github/workflows/renovate-hugo-modules.yml: -------------------------------------------------------------------------------- 1 | name: Renovate Hugo Modules 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | renovate_hugo_modules: 7 | name: Renovate Hugo Modules 8 | if: startsWith(github.head_ref, 'renovate/hugo-modules') 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout Pull Request HEAD Commit 12 | uses: actions/checkout@v4 13 | with: 14 | ref: ${{ github.event.pull_request.head.sha }} 15 | 16 | - name: Install Hugo 17 | uses: peaceiris/actions-hugo@v3 18 | with: 19 | hugo-version: "0.133.0" 20 | extended: true 21 | 22 | - name: Update All Hugo Modules 23 | run: hugo mod get -u 24 | 25 | - name: Tidy Hugo Modules 26 | run: hugo mod tidy 27 | 28 | - name: Display Changes 29 | run: git status 30 | 31 | - name: Commit Changes 32 | uses: stefanzweifel/git-auto-commit-action@v5 33 | with: 34 | commit_message: Renovate Hugo Modules 35 | commit_author: 36 | renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/hugo,node 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=hugo,node 3 | 4 | ### Hugo ### 5 | # Generated files by hugo 6 | /public/ 7 | /resources/_gen/ 8 | /assets/jsconfig.json 9 | hugo_stats.json 10 | 11 | # Executable may be added to repository 12 | hugo.exe 13 | hugo.darwin 14 | hugo.linux 15 | 16 | # Temporary lock file while building 17 | /.hugo_build.lock 18 | 19 | ### Node ### 20 | # Logs 21 | logs 22 | *.log 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | lerna-debug.log* 27 | .pnpm-debug.log* 28 | 29 | # Diagnostic reports (https://nodejs.org/api/report.html) 30 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 31 | 32 | # Runtime data 33 | pids 34 | *.pid 35 | *.seed 36 | *.pid.lock 37 | 38 | # Directory for instrumented libs generated by jscoverage/JSCover 39 | lib-cov 40 | 41 | # Coverage directory used by tools like istanbul 42 | coverage 43 | *.lcov 44 | 45 | # nyc test coverage 46 | .nyc_output 47 | 48 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 49 | .grunt 50 | 51 | # Bower dependency directory (https://bower.io/) 52 | bower_components 53 | 54 | # node-waf configuration 55 | .lock-wscript 56 | 57 | # Compiled binary addons (https://nodejs.org/api/addons.html) 58 | build/Release 59 | 60 | # Dependency directories 61 | node_modules/ 62 | jspm_packages/ 63 | 64 | # Snowpack dependency directory (https://snowpack.dev/) 65 | web_modules/ 66 | 67 | # TypeScript cache 68 | *.tsbuildinfo 69 | 70 | # Optional npm cache directory 71 | .npm 72 | 73 | # Optional eslint cache 74 | .eslintcache 75 | 76 | # Optional stylelint cache 77 | .stylelintcache 78 | 79 | # Microbundle cache 80 | .rpt2_cache/ 81 | .rts2_cache_cjs/ 82 | .rts2_cache_es/ 83 | .rts2_cache_umd/ 84 | 85 | # Optional REPL history 86 | .node_repl_history 87 | 88 | # Output of 'npm pack' 89 | *.tgz 90 | 91 | # Yarn Integrity file 92 | .yarn-integrity 93 | 94 | # dotenv environment variable files 95 | .env 96 | .env.development.local 97 | .env.test.local 98 | .env.production.local 99 | .env.local 100 | 101 | # parcel-bundler cache (https://parceljs.org/) 102 | .cache 103 | .parcel-cache 104 | 105 | # Next.js build output 106 | .next 107 | out 108 | 109 | # Nuxt.js build / generate output 110 | .nuxt 111 | dist 112 | 113 | # Gatsby files 114 | .cache/ 115 | # Comment in the public line in if your project uses Gatsby and not Next.js 116 | # https://nextjs.org/blog/next-9-1#public-directory-support 117 | # public 118 | 119 | # vuepress build output 120 | .vuepress/dist 121 | 122 | # vuepress v2.x temp and cache directory 123 | .temp 124 | 125 | # Docusaurus cache and generated files 126 | .docusaurus 127 | 128 | # Serverless directories 129 | .serverless/ 130 | 131 | # FuseBox cache 132 | .fusebox/ 133 | 134 | # DynamoDB Local files 135 | .dynamodb/ 136 | 137 | # TernJS port file 138 | .tern-port 139 | 140 | # Stores VSCode versions used for testing VSCode extensions 141 | .vscode-test 142 | 143 | # yarn v2 144 | .yarn/cache 145 | .yarn/unplugged 146 | .yarn/build-state.yml 147 | .yarn/install-state.gz 148 | .pnp.* 149 | 150 | ### Node Patch ### 151 | # Serverless Webpack directories 152 | .webpack/ 153 | 154 | # Optional stylelint cache 155 | 156 | # SvelteKit build / generate output 157 | .svelte-kit 158 | 159 | # End of https://www.toptal.com/developers/gitignore/api/hugo,node 160 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx lint-staged 2 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | public 4 | resources 5 | 6 | # Leave content from gohugoio/hugoBasicExample as is 7 | content/blog/emoji-support.md 8 | content/blog/markdown-syntax.md 9 | content/blog/math-typesetting.md 10 | content/blog/placeholder-text.md 11 | content/blog/rich-content.md -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | assets/css/critical/15-colors.css 2 | assets/css/non-critical/00-vendor.css 3 | assets/js/flexsearch.js 4 | assets/js/prism.js -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-go-template"], 3 | "proseWrap": "always", 4 | "overrides": [ 5 | { 6 | "files": ["*.html"], 7 | "options": { 8 | "parser": "go-template" 9 | } 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | assets/css/critical/15-colors.css 2 | assets/css/non-critical/00-vendor.css 3 | 4 | public 5 | resources -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["stylelint-prettier/recommended"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "csstools.postcss", 4 | "davidanson.vscode-markdownlint", 5 | "dbaeumer.vscode-eslint", 6 | "editorconfig.editorconfig", 7 | "esbenp.prettier-vscode", 8 | "stylelint.vscode-stylelint" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:1313", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "css.validate": false, 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "[html]": { 5 | "editor.defaultFormatter": "esbenp.prettier-vscode" 6 | }, 7 | "editor.formatOnSave": true, 8 | "editor.codeActionsOnSave": { 9 | "source.fixAll.eslint": "explicit", 10 | "source.fixAll.stylelint": "explicit" 11 | }, 12 | "emmet.includeLanguages": { 13 | "postcss": "css" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Michael Schnerring 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gruvbox Hugo Theme 2 | 3 | A retro-looking [Hugo](https://gohugo.io/) theme inspired by 4 | [gruvbox](https://github.com/morhetz/gruvbox) to build secure, fast, and 5 | SEO-ready websites. 6 | 7 | This theme is easily customizable with features that any coder loves. 8 | 9 | I took a lot of inspiration from the 10 | [Hello Friend](https://github.com/panr/hugo-theme-hello-friend) and 11 | [Doks](https://github.com/h-enk/doks) Hugo themes. 12 | 13 | ## DEMO [https://hugo-theme-gruvbox.schnerring.net/](https://hugo-theme-gruvbox.schnerring.net/) 14 | 15 | ![Screenshot of the theme in dark and light colors](https://raw.githubusercontent.com/schnerring/hugo-theme-gruvbox/main/images/tn.png) 16 | 17 | ## DISCLAIMER: Project Status 18 | 19 | This theme is still in early development. 20 | [Check out the issues](https://github.com/schnerring/hugo-theme-gruvbox/issues) 21 | to see what's still missing. 22 | 23 | ## Highlights 24 | 25 | - [Code highlighting with Prism](#prism) 26 | - Full-text search with [Flex Search](https://github.com/nextapps-de/flexsearch) 27 | - Display your CV using structured [JSON Resume](https://jsonresume.org/) data 28 | - [Integrated image optimization with next-gen image formats and lazy loading](#image-optimization) 29 | - Dark mode that also changes Prism themes 30 | - [Dynamic color choices from the Gruvbox color palette](#colors) 31 | - [Extensible to make it suit your needs](#extensibility) 32 | - Responsive, mobile-first design 33 | - Beautiful SVG icons with [Tabler Icons](https://tabler-icons.io/) 34 | 35 | A big thank you to the authors of the software that make this theme possible! ❤️ 36 | 37 | ## Quickstart 38 | 39 | The theme requires _extended_ Hugo because it uses Sass/SCSS. You'll also have 40 | to install Go because the theme uses Go modules. 41 | 42 | 1. `git clone` the repository and `cd` into it 43 | 2. Run `npm ci` to install the dependencies 44 | 3. Run `hugo server` 45 | 46 | ## Install The Theme 47 | 48 | Create a new Hugo website: 49 | 50 | ```shell 51 | hugo new site example.com 52 | cd example.com/ 53 | ``` 54 | 55 | Initialize the site as Hugo module 56 | 57 | ```shell 58 | hugo mod init example.com 59 | ``` 60 | 61 | Add the following to the `hugo.toml` file: 62 | 63 | ```toml 64 | [markup] 65 | # (Optional) To be able to use all Prism plugins, the theme enables unsafe 66 | # rendering by default 67 | #_merge = "deep" 68 | 69 | [build] 70 | # Merge build config of the theme 71 | _merge = "deep" 72 | 73 | # This hopefully will be simpler in the future. 74 | # See: https://github.com/schnerring/hugo-theme-gruvbox/issues/16 75 | [module] 76 | [[module.imports]] 77 | path = "github.com/schnerring/hugo-theme-gruvbox" 78 | [[module.imports]] 79 | path = "github.com/schnerring/hugo-mod-json-resume" 80 | [[module.imports.mounts]] 81 | # This will add the sample Richard Hendricks CV data 82 | source = "data" 83 | target = "data" 84 | [[module.imports.mounts]] 85 | source = "layouts" 86 | target = "layouts" 87 | [[module.imports.mounts]] 88 | source = "assets/css/json-resume.css" 89 | target = "assets/css/critical/44-json-resume.css" 90 | [[module.mounts]] 91 | # required by hugo-mod-json-resume 92 | source = "node_modules/simple-icons/icons" 93 | target = "assets/simple-icons" 94 | [[module.mounts]] 95 | source = "assets" 96 | target = "assets" 97 | [[module.mounts]] 98 | source = "layouts" 99 | target = "layouts" 100 | [[module.mounts]] 101 | source = "static" 102 | target = "static" 103 | [[module.mounts]] 104 | source = "node_modules/prismjs" 105 | target = "assets/prismjs" 106 | [[module.mounts]] 107 | source = "node_modules/prism-themes/themes" 108 | target = "assets/prism-themes" 109 | [[module.mounts]] 110 | source = "node_modules/typeface-fira-code/files" 111 | target = "static/fonts" 112 | [[module.mounts]] 113 | source = "node_modules/typeface-roboto-slab/files" 114 | target = "static/fonts" 115 | [[module.mounts]] 116 | source = "node_modules/@tabler/icons/icons" 117 | target = "assets/tabler-icons" 118 | [[module.mounts]] 119 | # Add hugo_stats.json to Hugo's server watcher 120 | source = "hugo_stats.json" 121 | target = "assets/watching/hugo_stats.json" 122 | ``` 123 | 124 | Install the theme: 125 | 126 | ```shell 127 | hugo mod get 128 | ``` 129 | 130 | Initialize the NPM `package.json` and install the dependencies: 131 | 132 | ```shell 133 | hugo mod npm pack 134 | npm install 135 | ``` 136 | 137 | Run Hugo: 138 | 139 | ```shell 140 | hugo server 141 | ``` 142 | 143 | ## Update The Theme 144 | 145 | Update the Hugo modules: 146 | 147 | ```shell 148 | hugo mod get -u 149 | hugo mod tidy 150 | ``` 151 | 152 | Update the NPM dependencies: 153 | 154 | ```shell 155 | hugo mod npm pack 156 | npm install 157 | ``` 158 | 159 | ## Colors 160 | 161 | Two options are available to configure the theme colors: 162 | 163 | - `defaultTheme`: `dark` or `light` (defaults to `light`) 164 | Default theme color for when a user visits the site for the first time. OS or 165 | user preference override this setting. 166 | [See this comment for more details.](https://github.com/schnerring/hugo-theme-gruvbox/issues/34#issuecomment-1235870375) 167 | - `themeColor`: `gray`, `red`, `green`, `yellow`, `blue`, `purple`, `aqua`, or 168 | `orange` (defaults to `blue`) 169 | Theme color for things such as links, headings etc. 170 | - `themeContrast`: `soft`, `medium`, or `hard` (defaults to `medium`) 171 | Theme background color 172 | 173 | ## Prism 174 | 175 | The theme allows customization of [Prism](https://prismjs.com/) via `hugo.toml` 176 | parameters: 177 | 178 | ```toml 179 | [params] 180 | [params.prism] 181 | languages = [ 182 | "markup", 183 | "css", 184 | "clike", 185 | "javascript" 186 | ] 187 | plugins = [ 188 | "normalize-whitespace", 189 | "toolbar", 190 | "copy-to-clipboard" 191 | ] 192 | ``` 193 | 194 | In my opinion, this is the coolest feature of the theme. Other Hugo themes 195 | usually include a pre-configured version of Prism, which complicates updates and 196 | change tracking, and clutters the theme's code base with third-party JavaScript. 197 | 198 | The Prism theme is not configurable because of the integration with the dark 199 | mode functionality. Toggling between color modes swaps the Prism theme between 200 | [`gruvbox-dark`](https://github.com/PrismJS/prism-themes/blob/master/themes/prism-gruvbox-dark.css) 201 | and 202 | [`gruvbox-light`](https://github.com/PrismJS/prism-themes/blob/master/themes/prism-gruvbox-light.css) 203 | from [github.com/PrismJS/prism-themes](https://github.com/PrismJS/prism-themes). 204 | 205 | Check out the 206 | [Prism showcase on the Demo site for examples](https://hugo-theme-gruvbox.schnerring.net/blog/prism-code-highlighting-showcase/) 207 | 208 | ### Explore Prism Features 209 | 210 | After running `npm install`, explore Prism features like this: 211 | 212 | ```shell 213 | # Languages 214 | ls node_modules/prismjs/components 215 | 216 | # Plugins 217 | ls node_modules/prismjs/plugins 218 | ``` 219 | 220 | ## Image Optimization 221 | 222 | Images are optimized by default without requiring 223 | [shortcodes](https://gohugo.io/content-management/shortcodes/). A 224 | [custom render hook](https://gohugo.io/getting-started/configuration-markup#markdown-render-hooks) 225 | does all the heavy lifting (see 226 | [render-image.html](./layouts/_default/_markup/render-image.html)). 227 | 228 | By default, the theme creates resized versions of images ranging from 300 to 700 229 | pixels wide in increments of 100 pixels. 230 | 231 | If the image format is not [WebP](https://en.wikipedia.org/wiki/WebP), the image 232 | is converted. The original file format will serve as a fallback for browsers 233 | that don't support the WebP format. 234 | 235 | Note that only images that are part of the 236 | [page bundle](https://gohugo.io/content-management/page-bundles/) are processed. 237 | If served from the `static/` directory or external sources, the image will be 238 | displayed but not be processed. 239 | 240 | Additionally, all images are lazily loaded to save the bandwidth of your users. 241 | 242 | ### Configuration 243 | 244 | The default quality is 75%. See the 245 | [official Image Processing Config Hugo docs](https://gohugo.io/content-management/image-processing/#image-processing-config). 246 | Change it by adding the following to the `hugo.toml` file: 247 | 248 | ```toml 249 | [imaging] 250 | quality = 75 251 | ``` 252 | 253 | Change the resize behavior: 254 | 255 | ```toml 256 | [params] 257 | [params.imageResize] 258 | min = 300 259 | max = 700 260 | increment = 100 261 | ``` 262 | 263 | ### Captions 264 | 265 | ```markdown 266 | ![Alt text](image-url.jpg "Caption with **markdown support**") 267 | ``` 268 | 269 | [The demo site features examples you can look at](https://hugo-theme-gruvbox.schnerring.net/blog/image-optimization/). 270 | I also use the theme for [my website](https://schnerring.net). 271 | 272 | ### Blog Post Covers 273 | 274 | Add blog post covers by defining them in the 275 | [front matter](https://gohugo.io/content-management/front-matter/) of your 276 | posts: 277 | 278 | ```markdown 279 | --- 280 | cover: 281 | src: my-blog-cover.jpg 282 | alt: A beautiful image containing interesting things 283 | caption: [Source](https://www.flickr.com/) 284 | --- 285 | ``` 286 | 287 | ## Embed Video Files 288 | 289 | Use the 290 | [video shortcode](https://github.com/schnerring/hugo-theme-gruvbox/blob/main/layouts/shortcodes/video.html) 291 | to embed your video files from 292 | [Page Resources](https://gohugo.io/content-management/page-resources/). 293 | 294 | With a page bundle looking like the following: 295 | 296 | ```text 297 | embed-videos/ 298 | |-- index.md 299 | |-- my-video.jpg 300 | |-- my-video.mp4 301 | |-- my-video.webm 302 | ``` 303 | 304 | You can embed `my-video` like this: 305 | 306 | ```markdown 307 | {{< video src="my-video" autoplay="true" controls="false" loop="true" >}} 308 | ``` 309 | 310 | The shortcode looks for media files matching the filename `my-video*`. For each 311 | `video` MIME type file, a `` element is added. The first `image` MIME 312 | type file is used as `poster` (thumbnail). It will render the following HTML: 313 | 314 | ```html 315 | 325 | ``` 326 | 327 | You can set a Markdown `caption`, wrapping the `