Add themes in-tree

This commit is contained in:
Dave Gallant
2024-01-07 08:49:07 -05:00
parent a5a09666f5
commit 604f3aaa4c
158 changed files with 19674 additions and 47 deletions

8
go.mod
View File

@@ -1,8 +0,0 @@
module davegallant.github.io
go 1.21
require (
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240106163015-62aac1ea9f6d // indirect
github.com/schnerring/hugo-mod-json-resume v0.0.0-20231224014047-e651a547c19a // indirect
)

6
go.sum
View File

@@ -1,6 +0,0 @@
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102231105-90b486f98b82 h1:X9jUG5D4OyxLjT3OTsL5TN3jFdpTXyLJJcRpLbgCsSc=
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102231105-90b486f98b82/go.mod h1:BQehNdf/SB/+bCc031OVsLECIgB9ZaN1dfUFKTeOIuo=
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240106163015-62aac1ea9f6d h1:HCGtUIiR/0t+PnuB/dYhU1ZX9rKNsEnc5EToW6vWLr0=
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240106163015-62aac1ea9f6d/go.mod h1:BQehNdf/SB/+bCc031OVsLECIgB9ZaN1dfUFKTeOIuo=
github.com/schnerring/hugo-mod-json-resume v0.0.0-20231224014047-e651a547c19a h1:EZRiOf0iW5k9lycVv3LngzSsGUxDRszYS4U7ea2r8RY=
github.com/schnerring/hugo-mod-json-resume v0.0.0-20231224014047-e651a547c19a/go.mod h1:5dixHC0WHu0w2Aqb8hsOCrIU1OBYr1w5Q6HZAmTub7Q=

View File

@@ -1,37 +1,4 @@
{ {
"comments": {
"dependencies": {
"@tabler/icons": "github.com/davegallant/hugo-theme-gruvbox",
"flexsearch": "github.com/davegallant/hugo-theme-gruvbox",
"normalize.css": "github.com/davegallant/hugo-theme-gruvbox",
"prism-themes": "github.com/davegallant/hugo-theme-gruvbox",
"prismjs": "github.com/davegallant/hugo-theme-gruvbox",
"simple-icons": "github.com/schnerring/hugo-mod-json-resume",
"typeface-fira-code": "github.com/davegallant/hugo-theme-gruvbox",
"typeface-roboto-slab": "github.com/davegallant/hugo-theme-gruvbox"
},
"devDependencies": {
"@fullhuman/postcss-purgecss": "github.com/davegallant/hugo-theme-gruvbox",
"cssnano": "github.com/davegallant/hugo-theme-gruvbox",
"eslint": "github.com/davegallant/hugo-theme-gruvbox",
"eslint-config-prettier": "github.com/davegallant/hugo-theme-gruvbox",
"eslint-plugin-prettier": "github.com/davegallant/hugo-theme-gruvbox",
"husky": "github.com/davegallant/hugo-theme-gruvbox",
"lint-staged": "github.com/davegallant/hugo-theme-gruvbox",
"markdownlint-cli": "github.com/davegallant/hugo-theme-gruvbox",
"postcss": "github.com/davegallant/hugo-theme-gruvbox",
"postcss-cli": "github.com/davegallant/hugo-theme-gruvbox",
"postcss-custom-media": "github.com/davegallant/hugo-theme-gruvbox",
"postcss-import": "github.com/davegallant/hugo-theme-gruvbox",
"postcss-nesting": "github.com/davegallant/hugo-theme-gruvbox",
"postcss-preset-env": "github.com/davegallant/hugo-theme-gruvbox",
"postcss-url": "github.com/davegallant/hugo-theme-gruvbox",
"prettier": "github.com/davegallant/hugo-theme-gruvbox",
"prettier-plugin-go-template": "github.com/davegallant/hugo-theme-gruvbox",
"stylelint": "github.com/davegallant/hugo-theme-gruvbox",
"stylelint-prettier": "github.com/davegallant/hugo-theme-gruvbox"
}
},
"dependencies": { "dependencies": {
"@tabler/icons": "^2.44.0", "@tabler/icons": "^2.44.0",
"flexsearch": "^0.7.31", "flexsearch": "^0.7.31",

View File

@@ -0,0 +1,7 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space

View File

@@ -0,0 +1,5 @@
assets/js/flexsearch.js
assets/js/prism.js
public
resources

View File

@@ -0,0 +1,14 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:prettier/recommended"
],
"parserOptions": {
"sourceType": "module"
}
}

View File

@@ -0,0 +1,43 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base"],
"rebaseWhen": "behind-base-branch",
"npm": {
"fileMatch": ["(^|/)package\\.hugo\\.json$"],
"rangeStrategy": "bump",
"updateLockFiles": false,
"ignoreTests": true,
"ignorePaths": [
"package.json",
"**/node_modules/**",
"**/bower_components/**",
"**/vendor/**",
"**/examples/**",
"**/__tests__/**",
"**/test/**",
"**/tests/**",
"**/__fixtures__/**"
]
},
"gomod": {
"fileMatch": ["(^|/)go\\.mod$"],
"rangeStrategy": "bump"
},
"packageRules": [
{
"matchManagers": ["gomod"],
"matchDepTypes": ["indirect"],
"enabled": true,
"groupName": "Hugo Modules"
},
{
"extends": "packages:linters",
"groupName": "linters"
},
{
"extends": "packages:postcss",
"groupName": "postcss packages"
}
]
}

View File

@@ -0,0 +1,84 @@
name: Publish Hugo Site
on:
push:
branches:
- main
jobs:
update_hugo_npm_dependencies:
name: Update Hugo npm Dependencies
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
commit_hash: ${{ steps.commit_changes.outputs.commit_hash }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Install Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: "0.111.3"
extended: true
- name: Write composite package.json
run: hugo mod npm pack
- name: Install npm Packages
run: npm install
- name: Display Changes
run: git status
- name: Commit Changes
id: commit_changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Update Hugo npm Dependencies
publish:
name: Publish Hugo Site
needs: update_hugo_npm_dependencies
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
ref: ${{ needs.update_hugo_npm_dependencies.outputs.commit_hash }}
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Install Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: "0.111.3"
extended: true
- name: Install npm Packages
run: npm ci
- name: Build Hugo
run: hugo --minify
- name: Deploy to Cloudflare Pages
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: hugo-theme-gruvbox
directory: ./public
gitHubToken: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,35 @@
name: Renovate Hugo Modules
on: pull_request
jobs:
renovate_hugo_modules:
name: Renovate Hugo Modules
if: startsWith(github.head_ref, 'renovate/hugo-modules')
runs-on: ubuntu-latest
steps:
- name: Checkout Pull Request HEAD Commit
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Install Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: "0.111.3"
extended: true
- name: Update All Hugo Modules
run: hugo mod get -u
- name: Tidy Hugo Modules
run: hugo mod tidy
- name: Display Changes
run: git status
- name: Commit Changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Renovate Hugo Modules
commit_author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

View File

@@ -0,0 +1,159 @@
# Created by https://www.toptal.com/developers/gitignore/api/hugo,node
# Edit at https://www.toptal.com/developers/gitignore?templates=hugo,node
### 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
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
# End of https://www.toptal.com/developers/gitignore/api/hugo,node

View File

@@ -0,0 +1 @@
_

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged

View File

@@ -0,0 +1,11 @@
node_modules
public
resources
# Leave content from gohugoio/hugoBasicExample as is
content/blog/emoji-support.md
content/blog/markdown-syntax.md
content/blog/math-typesetting.md
content/blog/placeholder-text.md
content/blog/rich-content.md

View File

@@ -0,0 +1,4 @@
assets/css/critical/15-colors.css
assets/css/non-critical/00-vendor.css
assets/js/flexsearch.js
assets/js/prism.js

View File

@@ -0,0 +1,12 @@
{
"plugins": ["prettier-plugin-go-template"],
"proseWrap": "always",
"overrides": [
{
"files": ["*.html"],
"options": {
"parser": "go-template"
}
}
]
}

View File

@@ -0,0 +1,5 @@
assets/css/critical/15-colors.css
assets/css/non-critical/00-vendor.css
public
resources

View File

@@ -0,0 +1,3 @@
{
"extends": ["stylelint-prettier/recommended"]
}

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Michael Schnerring
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.

View File

@@ -0,0 +1,577 @@
# Gruvbox Hugo Theme
A retro-looking [Hugo](https://gohugo.io/) theme inspired by
[gruvbox](https://github.com/morhetz/gruvbox) to build secure, fast, and
SEO-ready websites.
This theme is easily customizable with features that any coder loves.
I took a lot of inspiration from the
[Hello Friend](https://github.com/panr/hugo-theme-hello-friend) and
[Doks](https://github.com/h-enk/doks) Hugo themes.
## DEMO [https://hugo-theme-gruvbox.schnerring.net/](https://hugo-theme-gruvbox.schnerring.net/)
![Screenshot of the theme in dark and light colors](https://raw.githubusercontent.com/schnerring/hugo-theme-gruvbox/main/images/tn.png)
## DISCLAIMER: Project Status
This theme is still in early development.
[Check out the issues](https://github.com/schnerring/hugo-theme-gruvbox/issues)
to see what's still missing.
## Highlights
- [Code highlighting with Prism](#prism)
- Full-text search with [Flex Search](https://github.com/nextapps-de/flexsearch)
- Display your CV using structured [JSON Resume](https://jsonresume.org/) data
- [Integrated image optimization with next-gen image formats and lazy loading](#image-optimization)
- Dark mode that also changes Prism themes
- [Dynamic color choices from the Gruvbox color palette](#colors)
- [Extensible to make it suit your needs](#extensibility)
- Responsive, mobile-first design
- Beautiful SVG icons with [Tabler Icons](https://tabler-icons.io/)
A big thank you to the authors of the software that make this theme possible! ❤️
## Quickstart
The theme requires _extended_ Hugo because it uses Sass/SCSS. You'll also have
to install Go because the theme uses Go modules.
1. `git clone` the repository and `cd` into it
2. Run `npm ci` to install the dependencies
3. Run `hugo server`
## Install The Theme
Create a new Hugo website:
```shell
hugo new site example.com
cd example.com/
```
Initialize the site as Hugo module
```shell
hugo mod init example.com
```
Add the following to the `config.toml` file:
```toml
[markup]
# (Optional) To be able to use all Prism plugins, the theme enables unsafe
# rendering by default
#_merge = "deep"
[build]
# The theme enables writeStats which is required for PurgeCSS
_merge = "deep"
# This hopefully will be simpler in the future.
# See: https://github.com/schnerring/hugo-theme-gruvbox/issues/16
[module]
[[module.imports]]
path = "github.com/schnerring/hugo-theme-gruvbox"
[[module.imports]]
path = "github.com/schnerring/hugo-mod-json-resume"
[[module.imports.mounts]]
source = "data"
target = "data"
[[module.imports.mounts]]
source = "layouts"
target = "layouts"
[[module.imports.mounts]]
source = "assets/css/json-resume.css"
target = "assets/css/critical/44-json-resume.css"
[[module.mounts]]
# required by hugo-mod-json-resume
source = "node_modules/simple-icons/icons"
target = "assets/simple-icons"
[[module.mounts]]
source = "assets"
target = "assets"
[[module.mounts]]
source = "layouts"
target = "layouts"
[[module.mounts]]
source = "static"
target = "static"
[[module.mounts]]
source = "node_modules/prismjs"
target = "assets/prismjs"
[[module.mounts]]
source = "node_modules/prism-themes/themes"
target = "assets/prism-themes"
[[module.mounts]]
source = "node_modules/typeface-fira-code/files"
target = "static/fonts"
[[module.mounts]]
source = "node_modules/typeface-roboto-slab/files"
target = "static/fonts"
[[module.mounts]]
source = "node_modules/@tabler/icons/icons"
target = "assets/tabler-icons"
```
Install the theme:
```shell
hugo mod get
```
Initialize the NPM `package.json` and install the dependencies:
```shell
hugo mod npm pack
npm install
```
Run Hugo:
```shell
hugo server
```
## Update The Theme
Update the Hugo modules:
```shell
hugo mod get -u
hugo mod tidy
```
Update the NPM dependencies:
```shell
hugo mod npm pack
npm install
```
## Colors
Two options are available to configure the theme colors:
- `defaultTheme`: `dark` or `light` (defaults to `light`)
Default theme color for when a user visits the site for the first time. OS or
user preference override this setting.
[See this comment for more details.](https://github.com/schnerring/hugo-theme-gruvbox/issues/34#issuecomment-1235870375)
- `themeColor`: `gray`, `red`, `green`, `yellow`, `blue`, `purple`, `aqua`, or
`orange` (defaults to `blue`)
Theme color for things such as links, headings etc.
- `themeContrast`: `soft`, `medium`, or `hard` (defaults to `medium`)
Theme background color
## Prism
The theme allows customization of [Prism](https://prismjs.com/) via
`config.toml` parameters:
```toml
[params]
[params.prism]
languages = [
"markup",
"css",
"clike",
"javascript"
]
plugins = [
"normalize-whitespace",
"toolbar",
"copy-to-clipboard"
]
```
In my opinion, this is the coolest feature of the theme. Other Hugo themes
usually include a pre-configured version of Prism, which complicates updates and
change tracking, and clutters the theme's code base with third-party JavaScript.
The Prism theme is not configurable because of the integration with the dark
mode functionality. Toggling between color modes swaps the Prism theme between
[`gruvbox-dark`](https://github.com/PrismJS/prism-themes/blob/master/themes/prism-gruvbox-dark.css)
and
[`gruvbox-light`](https://github.com/PrismJS/prism-themes/blob/master/themes/prism-gruvbox-light.css)
from [github.com/PrismJS/prism-themes](https://github.com/PrismJS/prism-themes).
Check out the
[Prism showcase on the Demo site for examples](https://hugo-theme-gruvbox.schnerring.net/blog/prism-code-highlighting-showcase/)
### Explore Prism Features
After running `npm install`, explore Prism features like this:
```shell
# Languages
ls node_modules/prismjs/components
# Plugins
ls node_modules/prismjs/plugins
```
## Image Optimization
Images are optimized by default without requiring
[shortcodes](https://gohugo.io/content-management/shortcodes/). A
[custom render hook](https://gohugo.io/getting-started/configuration-markup#markdown-render-hooks)
does all the heavy lifting (see
[render-image.html](./layouts/_default/_markup/render-image.html)).
By default, the theme creates resized versions of images ranging from 300 to 700
pixels wide in increments of 100 pixels.
If the image format is not [WebP](https://en.wikipedia.org/wiki/WebP), the image
is converted. The original file format will serve as a fallback for browsers
that don't support the WebP format.
Note that only images that are part of the
[page bundle](https://gohugo.io/content-management/page-bundles/) are processed.
If served from the `static/` directory or external sources, the image will be
displayed but not be processed.
Additionally, all images are lazily loaded to save the bandwidth of your users.
### Configuration
The default quality is 75%. See the
[official Image Processing Config Hugo docs](https://gohugo.io/content-management/image-processing/#image-processing-config).
Change it by adding the following to the `config.toml` file:
```toml
[imaging]
quality = 75
```
Change the resize behavior:
```toml
[params]
[params.imageResize]
min = 300
max = 700
increment = 100
```
### Captions
```markdown
![Alt text](image-url.jpg "Caption with **markdown support**")
```
[The demo site features examples you can look at](https://hugo-theme-gruvbox.schnerring.net/blog/image-optimization/).
I also use the theme for [my website](https://schnerring.net).
### Blog Post Covers
Add blog post covers by defining them in the
[front matter](https://gohugo.io/content-management/front-matter/) of your
posts:
```markdown
---
cover:
src: my-blog-cover.jpg
alt: A beautiful image containing interesting things
caption: [Source](https://www.flickr.com/)
---
```
## Embed Video Files
Use the
[video shortcode](https://github.com/schnerring/hugo-theme-gruvbox/blob/main/layouts/shortcodes/video.html)
to embed your video files from
[Page Resources](https://gohugo.io/content-management/page-resources/).
With a page bundle looking like the following:
```text
embed-videos/
|-- index.md
|-- my-video.jpg
|-- my-video.mp4
|-- my-video.webm
```
You can embed `my-video` like this:
```markdown
{{< video src="my-video" autoplay="true" controls="false" loop="true" >}}
```
The shortcode looks for media files matching the filename `my-video*`. For each
`video` MIME type file, a `<source>` element is added. The first `image` MIME
type file is used as `poster` (thumbnail). It will render the following HTML:
```html
<video
autoplay
loop
poster="/blog/embed-videos/my-video.jpg"
width="100%"
playsinline
>
<source src="/blog/embed-videos/my-video.mp4" type="video/mp4" />
<source src="/blog/embed-videos/my-video.webm" type="video/webm" />
</video>
```
You can set a Markdown `caption`, wrapping the `<video>` inside a `<figure`>.
Additionally, the shortcode allows you to set the following attributes:
| Attribute | Default |
| ----------- | ------- |
| autoplay | `false` |
| controls | `true` |
| height | |
| loop | `false` |
| muted | `true` |
| preload | |
| width | `100%` |
| playsinline | `true` |
[Learn more about the `<video>` attributes here.](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attributes)
## SEO
Due to the
[European Copyright Directive](https://wayback.archive-it.org/12090/20210304045117/https://ec.europa.eu/digital-single-market/en/modernisation-eu-copyright-rules)
it is required to opt into displaying
[snippets](https://developers.google.com/search/docs/advanced/appearance/title-link?hl=en)
in search engine results.
By default, every page (except 404) includes the
`index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1`
robots meta value, opting into all snippet features.
You can override the robots meta value in the front matter of your pages:
```markdown
---
robots: noindex, nofollow
---
```
## Social Share Links
Configure social share links in the Hugo config like this:
```toml
[params]
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "facebook"
formatString = "https://www.facebook.com/sharer.php?u={url}"
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "reddit"
formatString = "https://reddit.com/submit?url={url}&title={title}"
[[params.socialShare]]
iconSuite = "tabler-icon"
iconName = "mail"
formatString = "mailto:?subject={title}&body={url}"
```
Use the `iconSuite` option to specify the icon suite used for the social share
link: `simple-icon` or `tabler-icon`. Select an icon from the suite with the
`iconName` option.
The `formatString` supports the following placeholders:
- `{url}` is replaced with the `.Permalink` of the post
- `{title}` is replaced with the `.Title` of the post
To enable social share links, set the following in the post's front matter:
```markdown
---
socialShare: true
---
```
Check out the
[Social Share URLs repo on GitHub](https://github.com/bradvin/social-share-urls)
for more format strings.
## Favicon
The favicons and [corresponding markup](./layouts/partials/head/favicons.html)
were generated with the free
[RealFaviconGenerator.net](https://realfavicongenerator.net/).
The easiest way to replace the default favicons is to generate them using
RealFaviconGenerator.net and put the generated files into the `static/`
directory.
## Extensibility
You can extend the theme by overriding the following partials in the
`layouts/partials` directory which by default are empty placeholder files:
- [`head/head_start.html`](./layouts/partials/head_start.html)
Custom HTML at the start of `<head>`
- [`head/head_end.html`](./layouts/partials/head_end.html)
Custom HTML at the end of `<head>`
- [`footer_end.html`](./layouts/partials/footer_end.html)
Custom HTML at the end of `<body>`
- [`comments.html`](./layouts/partials/comments.html)
Comments at the end of posts
### Example: Adding KaTeX Support to the Theme
[KaTeX](https://katex.org/) is a fast, easy-to-use JavaScript library for TeX
math rendering on the web. Let's add it to the theme via `npm`. First, add the
following to the `package.hugo.json` file:
```json
"dependencies": {
"katex": "^0.16.8"
}
```
Then run `hugo mod npm pack` to sync the `package.hugo.json` dependencies with
`package.json`. Run `npm install` after. We then need to mount the
`node_modules/katex` folder into Hugo's virtual filesystem by adding the
following to the `config/_default/module.toml` file:
```toml
[[mounts]]
source = "node_modules/katex"
target = "assets/katex"
```
We can then add the following to `layouts/partials/head/head_end.html`:
<!-- prettier-ignore-start -->
```html
{{ if .Params.katex }}
{{ $katexCSS := resources.Get "katex/dist/katex.min.css" }}
<link
rel="stylesheet"
href="{{ $katexCSS }}"
{{ if hugo.IsProduction }}
integrity="{{ $katexCSS.Data.Integrity }}"
{{ end }}
crossorigin="anonymous"
/>
{{ $katexJS := resources.Get "katex/dist/katex.min.js" }}
<script
defer
src="{{ $katexJS.RelPermalink }}"
{{ if hugo.IsProduction }}
integrity="{{ $katexJS.Data.Integrity }}"
{{ end }}
crossorigin="anonymous"
></script>
{{ $autoRender := resources.Get "katex/dist/contrib/auto-render.min.js" }}
<script
defer
src="{{ $autoRender.RelPermalink }}"
{{ if hugo.IsProduction }}
integrity="{{ $autoRender.Data.Integrity }}"
{{ end }}
crossorigin="anonymous"
onload="renderMathInElement(document.body);"
></script>
{{ end }}
```
<!-- prettier-ignore-end -->
The only thing left is enabling KaTeX in the front matter of our content:
```markdown
---
title: "Hello World"
description: "The first post of this blog"
date: 2021-03-14T15:00:21+01:00
draft: false
katex: true
---
I'm a .NET developer by trade, so let's say hello in C#!
```
## Configure the Tag Cloud
The theme comes with a tag cloud partial. It is included in the sidebar, but it
is disabled by default. If you wish to configure it, add the following to the
`[params]` section in the `config.toml` file:
```toml
[params.tagCloud]
enable = true
minFontSizeRem = 0.8
maxFontSizeRem = 2.0
```
## Remove the Sidebar
If you want to get rid of the sidebar, add an empty `data/en.json` file with the
following content:
```json
{
"$schema": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json",
"basics": {},
"work": [],
"volunteer": [],
"education": [],
"awards": [],
"certificates": [],
"publications": [],
"skills": [],
"languages": [],
"interests": [],
"references": [],
"projects": [],
"meta": {
"canonical": "https://raw.githubusercontent.com/jsonresume/resume-schema/master/resume.json",
"version": "v1.0.0",
"lastModified": "2017-12-24T15:53:00"
}
}
```
## Extend CSS
The theme uses PostCSS with following plugins:
- [postcss-import](https://github.com/postcss/postcss-import)
- [postcss-url](https://github.com/postcss/postcss-url)
- [postcss-nesting](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting)
- [postcss-custom-media](https://github.com/postcss/postcss-custom-media)
Additionally the following plugins are used if building the site with
`hugo -e production`:
- [postcss-preset-env](https://github.com/csstools/postcss-plugins/tree/main/plugin-packs/postcss-preset-env)
- [cssnano](https://github.com/cssnano/cssnano) for minification
- [@fullhuman/postcss-purgecss](https://github.com/FullHuman/purgecss)
Inside the `assets/css` two folders exist, `critical` and `non-critical`. Files
inside `critical` are concatenated during build time and inlined into the
`<head>` element. The styles target mostly
[above the fold content](https://en.wikipedia.org/wiki/Above_the_fold#In_web_design).
Try to keep inline CSS to a minimum because it can't be cached and will be
inlined into every single page. Files inside `non-critical` are concatenated
into a single file and included as `<style>`. Most of the styles are in there.
Files are concatenated in lexicographic order of their file names. File names
start with two digits and a hyphen: `NN-`. The order of files might differ
between Linux and Windows, so using this convention improves cross-platform
compatibility.
[You might know this approach if you're familiar with Xorg](https://wiki.archlinux.org/title/Xorg#Using_.conf_files).
You can add new CSS files to the PostCSS pipeline like this:
- `critical/50-foo.css`
- `non-critical/05-bar.css`
- `non-critical/99-last.css`

View File

@@ -0,0 +1,10 @@
---
title: "{{ humanize .Name | title }}"
date: "{{ .Date }}"
draft: true
comments: false
socialShare: true
toc: false
cover:
src: cover.png
---

View File

@@ -0,0 +1,28 @@
/*
Critical CSS for above-the-fold content, delivered inline to increase first
paint performance
*/
/*! purgecss start ignore */
/* Typefaces */
@import "typeface-roboto-slab";
@import "typeface-fira-code";
/* Normalize */
@import "normalize.css/normalize.css";
/*! purgecss end ignore */
/*! CC BY-SA 3.0 License | https://stackoverflow.com/a/36118384/1154965 */
@keyframes blink {
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* Default license header for non-vendor CSS source code that follows */
/*! MIT License | github.com/schnerring/hugo-theme-gruvbox */

View File

@@ -0,0 +1,9 @@
/*
Bootstrap 5 breakpoints
See: https://getbootstrap.com/docs/5.0/layout/breakpoints/#available-breakpoints
*/
@custom-media --sm (min-width: 576px);
@custom-media --md (min-width: 768px);
@custom-media --lg (min-width: 992px);
@custom-media --xl (min-width: 1200px);
@custom-media --xxl (min-width: 1400px);

View File

@@ -0,0 +1,87 @@
{{ $themeContrast := .Param "themeContrast" | default "medium" }}
{{ $backgroundColor := "bg0" }}
{{ if eq $themeContrast "soft" }}
{{ $backgroundColor = "bg0_s" }}
{{ else if eq $themeContrast "hard" }}
{{ $backgroundColor = "bg0_h" }}
{{ end }}
:root[data-theme="light"] {
--bg: var(--{{ $backgroundColor }});
--bg0: #fbf1c7;
--bg0_h: #f9f5d7;
--bg0_s: #f2e5bc;
--bg1: #ebdbb2;
--bg2: #d5c4a1;
--bg3: #bdae93;
--bg4: #a89984;
--fg: var(--fg1);
--fg0: #282828;
--fg1: #3c3836;
--fg2: #504945;
--fg3: #665c54;
--fg4: #7c6f64;
--gray1: var(--fg4);
--gray2: #928374;
--red1: #cc241d;
--red2: #9d0006;
--green1: #98971a;
--green2: #797403;
--yellow1: #d79921;
--yellow2: #b57614;
--blue1: #458588;
--blue2: #076678;
--purple1: #b16286;
--purple2: #8f3f71;
--aqua1: #689d6a;
--aqua2: #427b58;
--orange1: #d65d0e;
--orange2: #af3a03;
& .light--hidden {
display: none;
}
}
:root[data-theme="dark"] {
--bg: var(--{{ $backgroundColor }});
--bg0: #282828;
--bg0_h: #1d2021;
--bg0_s: #32302f;
--bg1: #3c3836;
--bg2: #504945;
--bg3: #665c54;
--bg4: #7c6f64;
--fg: var(--fg1);
--fg0: #fbf1c7;
--fg1: #ebdbb2;
--fg2: #d5c4a1;
--fg3: #bdae93;
--fg4: #a89984;
--gray1: var(--fg4);
--gray2: #928374;
--red1: #cc241d;
--red2: #fb4934;
--green1: #98971a;
--green2: #b8bb26;
--yellow1: #d79921;
--yellow2: #fabd2f;
--blue1: #458588;
--blue2: #83a598;
--purple1: #b16286;
--purple2: #d3869b;
--aqua1: #689d6a;
--aqua2: #8ec07c;
--orange1: #d65d0e;
--orange2: #fe8019;
& .dark--hidden {
display: none;
}
}
:root {
{{ $themeColor := .Param "themeColor" | default "blue" }}
--primary: var(--{{ $themeColor }}1);
--primary-alt: var(--{{ $themeColor }}2);
}

View File

@@ -0,0 +1,238 @@
:root {
--font-monospace: "Fira Code", "Lucida Console", Monaco, monospace;
--font-sans-serif: Verdana, Helvetica, sans-serif;
--font-serif: "Roboto Slab", Georgia, serif;
}
html {
font-family: var(--font-serif);
font-size: 1rem;
scroll-behavior: smooth;
}
body {
background: var(--bg);
color: var(--fg);
line-height: 1.675;
word-wrap: break-word;
}
strong {
letter-spacing: 0.35px;
}
a {
color: inherit;
text-decoration: none;
}
a.link--external::after {
/* 2009 = Thin Space */
content: "\2009↗";
}
img,
video {
border: 2px solid var(--bg1);
height: auto;
max-width: 100%;
}
figure {
display: inline-block;
}
figcaption {
color: var(--fg3);
font-family: var(--font-serif);
font-size: 0.9rem;
}
*::selection {
color: var(--fg0);
background: var(--bg4);
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: var(--fg0);
font-family: var(--font-monospace);
font-weight: 300;
line-height: 1.4;
& code {
font-size: 1em;
}
}
h2,
h3,
h4,
h5,
h6 {
border-bottom: 1px solid var(--bg1);
}
h1,
h2 {
font-weight: 400;
}
h1 {
font-size: 1.875rem;
}
h2 {
font-size: 1.75rem;
}
h3 {
font-size: 1.625rem;
}
@media (--md) {
h1 {
font-size: 2.375rem;
}
h2 {
font-size: 2rem;
}
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: 1.5rem;
}
h5 {
font-size: 1.375rem;
}
h6 {
font-size: 1.25rem;
}
table {
table-layout: fixed;
border-collapse: collapse;
width: 100%;
margin: 2rem 0;
}
table,
th,
td {
border: 1px solid var(--bg1);
padding: 0.5rem;
}
hr {
border: none;
background: var(--bg1);
height: 1px;
margin: 3rem auto;
width: 80%;
}
mark {
background: var(--yellow1);
color: var(--bg0);
}
abbr {
text-underline-offset: 0.2rem;
}
blockquote,
code,
kbd,
mark,
pre {
border-radius: 0.2rem;
padding: 0 0.2em;
}
pre code {
padding: 0;
}
blockquote,
code,
kbd,
pre,
th {
background: var(--bg1);
}
code,
kbd,
pre,
th {
font-family: var(--font-monospace);
}
code,
kbd {
& code,
& kbd {
background: var(--bg2);
}
}
blockquote,
pre {
padding: 1rem;
}
pre {
/* TODO is !important really needed because of Prism? */
background: var(--bg1) !important;
overflow: auto;
& code {
background: none;
}
}
blockquote,
blockquote.twitter-tweet {
border-left: var(--primary-alt) 5px solid;
margin: 0.5rem 0;
& code {
background: var(--bg2);
}
& p:first-of-type {
margin-top: 0;
}
& p:last-of-type {
margin-bottom: 0;
}
}
blockquote.twitter-tweet {
border-color: var(--blue2);
color: inherit;
font: inherit;
font-size: inherit;
line-height: inherit;
& a {
color: var(--blue2);
}
& a:hover {
color: var(--blue1);
text-decoration: none !important;
}
}

View File

@@ -0,0 +1,14 @@
pre::-webkit-scrollbar {
height: 0.5rem;
scrollbar-width: auto;
}
pre::-webkit-scrollbar-track {
background: var(--bg2);
border-radius: 0.2rem;
}
pre::-webkit-scrollbar-thumb {
background: var(--bg4);
border-radius: 0.2rem;
}

View File

@@ -0,0 +1,48 @@
.layout {
display: grid;
grid-template-areas:
"header"
"main"
"footer";
grid-template-rows: auto 1fr auto;
height: 100vh;
}
main {
align-items: start;
display: grid;
grid-area: main;
grid-template-areas: "empty content sidebar";
grid-template-columns: 1fr minmax(0, 650px) 4fr;
}
header {
background: var(--bg1);
grid-area: header;
}
footer {
grid-area: footer;
}
main,
footer {
margin: 0.5em 1.1em;
}
.content {
grid-area: content;
}
.sidebar {
display: none;
flex-direction: column;
grid-area: sidebar;
margin-top: 3rem;
position: sticky;
top: 2rem;
@media (--lg) {
display: flex;
}
}

View File

@@ -0,0 +1,285 @@
header {
display: grid;
font-family: var(--font-monospace);
font-size: 1.125rem;
grid-template-columns: auto auto 1fr auto;
grid-template-areas: "heading search nav theme-toggle";
padding: 0.75rem;
}
.logo {
color: var(--fg0);
display: flex;
font-weight: 700;
grid-area: heading;
&:hover .logo__cursor {
animation: 1s blink infinite;
opacity: 1;
}
}
.logo__chevron,
.logo__cursor {
margin-left: 0.5rem;
}
.logo__cursor {
opacity: 0;
}
.logo__text {
display: none;
}
@media (--md) {
.logo__text {
display: block;
}
}
/*! purgecss start ignore */
.search {
display: flex;
grid-area: search;
margin: 0 1rem;
}
#search__text {
border: 1px solid var(--bg2);
border-radius: 0.2rem;
background: var(--bg2);
caret-color: var(--fg);
color: var(--fg);
outline: none;
padding: 0 0.5rem;
width: 100%;
&:hover {
border-color: var(--bg3);
}
&:focus {
border-color: var(--bg4);
}
&::placeholder {
color: var(--fg3);
}
&[type="search"]::-webkit-search-cancel-button {
appearance: none;
}
}
#search__suggestions {
background: var(--bg);
border-radius: 0.2rem;
box-shadow: 0 0.5rem 1rem var(--bg1);
font-family: var(--font-serif);
left: 0;
margin-top: 2rem;
position: absolute;
width: 95vw;
z-index: 1000;
}
@media (--md) {
.search {
position: relative;
}
#search__suggestions {
width: 60vw;
}
}
.search__suggestions--hidden {
display: none;
}
.search__suggestion-item {
border-bottom: 1px dashed var(--bg2);
display: grid;
grid-template-columns: 1fr 2fr;
&:focus,
&:focus-visible,
&:hover {
background: var(--bg1);
cursor: pointer;
outline: none;
}
&:last-child {
border: none;
}
}
.search__suggestion-title,
.search__suggestion-description {
padding: 0 1rem;
margin: 1rem 0;
}
.search__suggestion-title {
font-weight: 700;
}
.search__suggestion-description {
border-left: 1px solid var(--bg2);
}
.search__no-results {
padding: 0.75rem;
}
/*! purgecss end ignore */
.theme__toggle {
align-items: center;
background: none;
border: none;
color: var(--yellow1);
cursor: pointer;
display: flex;
grid-area: theme-toggle;
margin: 0 1rem;
&:hover {
color: var(--yellow2);
}
& svg {
height: 28px;
width: 28px;
}
}
/* TODO: simplify deep nesting */
nav#menu {
align-items: center;
display: flex;
grid-area: nav;
justify-content: flex-end;
& .menu__item {
color: var(--fg);
&:hover {
color: var(--fg3);
cursor: pointer;
}
}
& ul {
list-style: none;
margin: 0;
padding: 0;
}
& ul.menu--horizontal {
align-items: center;
display: none;
& li {
display: inline-block;
margin: 0 0.75rem;
}
@media (--md) {
display: flex;
}
}
& ul.menu--vertical {
background: var(--fg0);
bottom: 0;
margin: 0;
padding: 3rem;
position: fixed;
right: 0;
top: 0;
transform: translate(100%, 0);
transition: transform 0.5s cubic-bezier(0.9, 0, 0.1, 1);
width: 50%;
z-index: 10;
& .menu__item {
color: var(--bg1);
&:hover {
color: var(--bg4);
}
}
}
& .menu__burger {
display: flex;
height: 24px;
width: 24px;
& > * {
position: absolute;
}
& svg {
width: inherit;
z-index: 20;
height: inherit;
& line {
transition-duration: 0.5s;
transition-property: stroke, opacity, transform;
transition-timing-function: cubic-bezier(0.9, 0, 0.1, 1);
}
& line:nth-of-type(1) {
transform-origin: center 6px;
}
& line:nth-of-type(2) {
transform-origin: center 12px;
}
& line:nth-of-type(3) {
transform-origin: center 18px;
}
}
& input {
height: inherit;
opacity: 0;
width: inherit;
z-index: 30;
&:checked {
& ~ ul.menu--vertical {
transform: none;
}
& ~ svg {
stroke: var(--bg1);
& line:nth-of-type(1) {
transform: translate(0, 6px) rotate(45deg);
}
& line:nth-of-type(2) {
opacity: 0;
transform: scale(0.2);
}
& line:nth-of-type(3) {
transform: translate(0, -6px) rotate(-45deg);
}
}
}
}
@media (--md) {
display: none;
}
}
}

View File

@@ -0,0 +1,83 @@
.sidebar {
font-family: var(--font-monospace);
max-width: 350px;
margin-left: auto;
margin-right: auto;
padding-left: 2.5rem;
& hr {
margin: 1.5rem auto;
}
& svg {
fill: var(--fg);
}
}
.sidebar__heading {
font-size: 1.3rem;
}
aside.toc {
& a {
color: var(--primary-alt);
}
& a:hover {
color: var(--primary);
}
& ul {
list-style: none;
margin: 0;
padding: 0;
& ul {
font-size: 0.9rem;
margin-left: 0.5rem;
}
& li {
line-height: 1.1;
& a {
display: block;
padding: 0.2rem 0;
}
}
}
}
.jr-basics__image {
background: var(--bg1);
border: 2px solid var(--bg2);
}
.jr-basics__summary {
color: var(--fg3);
font-family: var(--font-serif);
margin: 0.75rem 0;
}
.jr-basics__profile {
& a:hover {
color: var(--fg3);
& svg {
fill: var(--fg3);
}
}
}
.tag-cloud {
line-height: 1.1;
text-align: justify;
}
.tag-cloud__tag:hover {
color: var(--fg3);
}
.tag-cloud__tag--active {
text-decoration: underline;
}

View File

@@ -0,0 +1,75 @@
.post,
.content-section {
border-bottom: 2px dotted var(--bg1);
padding: 2rem 0;
}
.post {
& figure,
& img:not(figure img),
& video:not(figure video) {
margin: 0.5rem 0;
box-sizing: border-box;
}
}
.post-header,
.post-content__read-more {
font-family: var(--font-monospace);
}
.post-meta__author {
font-weight: 700;
}
.post-content {
margin: 1.3rem 0;
}
.post-content__read-more {
color: var(--primary-alt);
margin-top: 1.3rem;
}
.post-header,
.post-content {
& a {
color: var(--primary-alt);
}
& a:hover {
color: var(--primary);
}
}
.post-tags {
align-items: center;
display: flex;
flex-wrap: wrap;
gap: 0.9rem;
margin: 1rem 0;
}
.post-tag {
font-size: 0.9rem;
line-height: 1;
&::before {
content: "#";
}
}
.post-heading__anchor {
display: none;
}
h1:hover,
h2:hover,
h3:hover,
h4:hover,
h5:hover,
h6:hover {
& .post-heading__anchor {
display: inline-block;
}
}

View File

@@ -0,0 +1,29 @@
.jr__item-meta {
font-family: var(--font-monospace);
}
.jr__item-meta {
align-items: start;
flex-flow: column;
}
@media (--md) {
.jr__item-meta {
align-items: center;
flex-flow: row wrap;
}
.jr__date,
.jr__date-range,
.jr-work__location {
flex-grow: 1;
text-align: right;
}
.jr-awards__awarder,
.jr-publications__publisher,
.jr-education__institution,
.jr-volunteer__organization {
flex-basis: 100%;
}
}

View File

@@ -0,0 +1,68 @@
.social-share {
align-items: center;
border-top: 2px dotted var(--bg1);
display: flex;
flex-wrap: wrap;
gap: 0.9rem;
margin: 3rem 0;
padding-top: 3rem;
& svg {
fill: var(--fg);
width: 24px;
height: 24px;
&.icon-tabler {
fill: none;
stroke: var(--fg);
}
}
}
.social-share__item {
background: var(--bg1);
padding: 0.5rem;
display: flex;
}
/* Sticky social bar left
@media (--xl) {
.social-share {
align-items: flex-start;
border-top: none;
flex-direction: column;
position: fixed;
top: 15%;
left: 0;
}
.social-share__heading {
display: none;
}
.social-share__item {
transition: padding 0.2s ease-in;
&:hover {
padding: 0.5rem 1rem;
}
& svg {
&:hover {
fill: var(--fg);
}
&.icon-tabler {
fill: none;
&:hover {
stroke: var(--fg);
}
}
}
}
}
*/

View File

@@ -0,0 +1,24 @@
/*! purgecss start ignore */
/* Prism Plugins */
{{ range $.Site.Params.prism.plugins }}
{{ $path := printf "prismjs/plugins/%s/prism-%s.css" . . }}
{{ $plugin := resources.Get $path }}
{{ if $plugin }}
{{ printf "/*%s*/" $plugin }}
@import "{{ $plugin }}";
{{ end }}
{{ end }}
/* Prism Font */
code,
kbd,
code[class*="language-"],
pre[class*="language-"] {
font-family: var(--font-monospace);
}
/*! purgecss end ignore */
/* Default license header for non-vendor CSS source code that follows */
/*! MIT License | github.com/schnerring/hugo-theme-gruvbox */

View File

@@ -0,0 +1,9 @@
/*
Bootstrap 5 breakpoints
See: https://getbootstrap.com/docs/5.0/layout/breakpoints/#available-breakpoints
*/
@custom-media --sm (min-width: 576px);
@custom-media --md (min-width: 768px);
@custom-media --lg (min-width: 992px);
@custom-media --xl (min-width: 1200px);
@custom-media --xxl (min-width: 1400px);

View File

@@ -0,0 +1,11 @@
footer {
align-items: center;
color: var(--fg3);
display: flex;
font-family: var(--font-monospace);
font-size: 0.8rem;
justify-content: center;
padding-bottom: 0.5rem;
padding-top: 2rem;
text-align: center;
}

View File

@@ -0,0 +1,18 @@
.pagination {
display: flex;
margin-top: 2rem;
}
.pagination__button {
color: var(--primary-alt);
font-family: var(--font-monospace);
font-size: 1.125rem;
}
.pagination__button:hover {
color: var(--primary);
}
.pagination__button--next {
margin-left: auto;
}

View File

@@ -0,0 +1,47 @@
function getTheme() {
if (localStorage && localStorage.getItem("theme")) {
// User preference
return localStorage.getItem("theme");
}
if (window.matchMedia) {
// OS preference
return window.matchMedia("(prefers-color-scheme: light)").matches
? "light"
: "dark";
}
// Undefined
}
function setTheme(theme) {
// Main theme
document.documentElement.setAttribute("data-theme", theme);
// Prism theme
const prismDark = document.getElementById("prism-dark");
const prismLight = document.getElementById("prism-light");
prismDark.toggleAttribute("disabled", theme === "light");
prismLight.toggleAttribute("disabled", theme === "dark");
// Store user preference
localStorage.setItem("theme", theme);
}
// Initial load
const theme = getTheme();
if (theme) setTheme(theme);
function toggleTheme(e) {
const theme = e.currentTarget.classList.contains("light--hidden")
? "light"
: "dark";
setTheme(theme);
}
// This script is inlined in the <head> of the document, so we have to wait
// for the DOM content before can add event listeners to the toggle buttons
document.addEventListener("DOMContentLoaded", function () {
const toggleButtons = document.querySelectorAll(".theme__toggle");
toggleButtons.forEach((btn) => {
btn.addEventListener("click", toggleTheme);
});
});

View File

@@ -0,0 +1,137 @@
//! Source: https://github.com/h-enk/doks/blob/master/assets/js/index.js
import { Document } from "flexsearch";
const search = document.getElementById("search__text");
const suggestions = document.getElementById("search__suggestions");
if (search !== null) {
document.addEventListener("keydown", (e) => {
if (e.ctrlKey && e.key === "/") {
// Focus search bar with CTRL + /
e.preventDefault();
search.focus();
} else if (e.key === "Escape") {
// Unfocus search bar with ESC
search.blur();
suggestions.classList.add("search__suggestions--hidden");
}
});
}
document.addEventListener("click", (e) => {
const clickInsideSuggestions = suggestions.contains(e.target);
if (!clickInsideSuggestions) {
// Hide search suggestions if clicking elsewhere
suggestions.classList.add("search__suggestions--hidden");
}
});
/*! Source: https://dev.to/shubhamprakash/trap-focus-using-javascript-6a3 */
document.addEventListener("keydown", (e) => {
const suggestionsHidden = suggestions.classList.contains(
"search__suggestions--hidden"
);
if (suggestionsHidden) return;
const focusableSuggestions = [...suggestions.querySelectorAll("a")];
if (focusableSuggestions.length === 0) return;
const currentIndex = focusableSuggestions.indexOf(document.activeElement);
if (e.key === "ArrowDown") {
// Focus next suggestion
e.preventDefault();
const nextIndex =
currentIndex + 1 < focusableSuggestions.length
? currentIndex + 1
: currentIndex;
focusableSuggestions[nextIndex].focus();
} else if (e.key === "ArrowUp") {
// Focus previous suggestion
e.preventDefault();
nextIndex = currentIndex > 0 ? currentIndex - 1 : 0;
focusableSuggestions[nextIndex].focus();
}
});
(function () {
const index = new Document({
tokenize: "forward",
cache: 100,
document: {
id: "id",
store: ["href", "title", "description"],
index: ["title", "description", "content"],
},
});
//! Source: https://discourse.gohugo.io/t/range-length-or-last-element/3803/2
{{ $list := (where .Site.RegularPages "Type" "in" .Site.Params.mainSections) }}
{{ $len := (len $list) }}
index.add(
{{ range $index, $element := $list }}
{
id: {{ $index }},
href: "{{ .RelPermalink }}",
title: {{ .Title | jsonify }},
{{ with .Description }}
description: {{ . | jsonify }},
{{ else }}
description: {{ .Summary | plainify | jsonify }},
{{ end }}
content: {{ .Plain | jsonify }}
})
{{ if ne (add $index 1) $len }}
.add(
{{ end }}
{{ end }}
{{ if eq 0 $len }}
)
{{ end }}
;
search.addEventListener("input", function () {
const maxResultsCount = {{ $.Site.Params.flexsearch.maxResultsCount | default 5 }};
const searchText = this.value;
const searchResults = index.search(searchText, maxResultsCount, { enrich: true });
const searchResultsMap = new Map();
// Deduplicate search results by href
for (const searchResult of searchResults.flatMap((r) => r.result)) {
if (searchResultsMap.has(searchResult.href)) continue;
searchResultsMap.set(searchResult.doc.href, searchResult.doc);
}
suggestions.innerHTML = "";
suggestions.classList.remove("search__suggestions--hidden");
if (searchResultsMap.size === 0 && searchText) {
const noResultsMessage = document.createElement("div")
noResultsMessage.innerHTML = `No results for "<strong>${searchText}</strong>"`
noResultsMessage.classList.add("search__no-results");
suggestions.appendChild(noResultsMessage);
return;
}
for (const [href, searchResult] of searchResultsMap) {
const suggestion = document.createElement("a");
suggestion.href = href;
suggestion.classList.add("search__suggestion-item");
suggestions.appendChild(suggestion);
const title = document.createElement("div");
title.textContent = searchResult.title;
title.classList.add("search__suggestion-title");
suggestion.appendChild(title);
const description = document.createElement("div");
description.textContent = searchResult.description;
description.classList.add("search__suggestion-description");
suggestion.appendChild(description);
if (suggestions.childElementCount === maxResultsCount) break;
}
});
})();

View File

@@ -0,0 +1,11 @@
import Prism from "prismjs";
{{ range $.Site.Params.prism.languages }}
import "prismjs/components/prism-{{ . }}";
{{ end }}
{{ range $.Site.Params.prism.plugins }}
import "prismjs/plugins/{{ . }}/prism-{{ . }}";
{{ end }}
Prism.highlightAll();

View File

@@ -0,0 +1,2 @@
noJSConfigInAssets = true
writeStats = true

View File

@@ -0,0 +1,150 @@
baseURL = "http://localhost"
copyright = "Copyright © 2021"
title = "hugo-theme-gruvbox"
#paginate = 10
enableRobotsTXT = true
# Enable to calculate the last modified date from Git history and show it in the post header
#enableGitInfo = true
[markup]
[markup.goldmark]
[markup.goldmark.renderer]
# This setting allows inlining <script> and <style> tags in markdown,
# which is useful and required to use Prism plugins, but may be dangerous
# if the content isn't trustworthy.
unsafe = true
[imaging]
# JPEG and WebP image processing quality, defaults to 75%
#quality = 75
[params]
# dark or light, defaults to light. Local storage and OS preference override this param. For more details see:
# https://github.com/schnerring/hugo-theme-gruvbox/issues/34#issuecomment-1235870375
#defaultTheme = "light"
# gray red green yellow blue purple aqua orange, defaults to blue
#themeColor = "blue"
# soft medium hard, defaults to medium
#themeContrast = "medium"
author = "Michael Schnerring"
subtitle = "Theme Demo"
description = "A retro-looking Hugo theme inspired by gruvbox. The pastel colors are high contrast, easily distinguishable, pleasing to the eye, and feature light and dark color palettes."
[params.logo]
text = "gruvbox"
url = "/"
[params.prism]
languages = [
"markup",
"css",
"clike",
"javascript",
"bash",
"diff",
"toml"
]
plugins = [
"normalize-whitespace",
"toolbar",
"copy-to-clipboard",
"line-numbers",
"command-line",
"diff-highlight"
]
# By default, the theme creates resized versions of images ranging from 300 to
# 700 pixels wide in increments of 200 pixels
#[params.imageResize]
# min = 300
# max = 700
# increment = 200
[params.tagCloud]
enable = false
minFontSizeRem = 0.8
maxFontSizeRem = 2.0
# Social share links for posts:
# - iconSuite: "simple-icon" or "tabler-icon"
# - iconName: name of the icon from the "iconSuite"
# - {url} placeholder for post .Permalink
# - {title} placeholder for post .Title
# See https://github.com/bradvin/social-share-urls for more format strings
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "facebook"
formatString = "https://www.facebook.com/sharer.php?u={url}"
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "reddit"
formatString = "https://reddit.com/submit?url={url}&title={title}"
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "twitter"
formatString = "https://twitter.com/intent/tweet?url={url}&text={title}"
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "linkedin"
formatString = "https://www.linkedin.com/sharing/share-offsite/?url={url}"
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "whatsapp"
formatString = "whatsapp://send/?text={title}%20{url}"
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "tumblr"
formatString = "https://www.tumblr.com/widgets/share/tool?canonicalUrl={url}&title={title}&caption={title}"
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "pinterest"
formatString = "http://pinterest.com/pin/create/button/?url={url}"
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "ycombinator"
formatString = "https://news.ycombinator.com/submitlink?u={url}&t={title}"
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "vk"
formatString = "http://vk.com/share.php?url={url}&title={title}&comment={title}"
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "xing"
formatString = "https://www.xing.com/spi/shares/new?url={url}"
[[params.socialShare]]
iconSuite = "simple-icon"
iconName = "telegram"
formatString = "https://telegram.me/share/url?url={url}&text={title}"
[[params.socialShare]]
iconSuite = "tabler-icon"
iconName = "mail"
formatString = "mailto:?subject={title}&body={url}"
[menu]
[[menu.main]]
identifier = "blog"
name = "Blog"
url = "/blog"
weight = 10
[[menu.main]]
identifier = "cv"
name = "CV"
url = "/cv"
weight = 20
[[menu.main]]
identifier = "about"
name = "About"
url = "/about"
weight = 30
[languages]
[languages.en]
languageName = "English"
weight = 10
[languages.en.params.jsonResume]
present = "present"
[languages.de]
languageName = "Deutsch"
weight = 20
[languages.de.params.jsonResume]
present = "heute"

View File

@@ -0,0 +1,44 @@
# Module debugging
#replacements = "github.com/schnerring/hugo-mod-json-resume -> ../../hugo-mod-json-resume"
[hugoVersion]
extended = true
min = "0.84.0"
[[imports]]
path = "github.com/schnerring/hugo-mod-json-resume"
[[imports.mounts]]
source = "data"
target = "data"
[[imports.mounts]]
source = "layouts"
target = "layouts"
[[imports.mounts]]
source = "assets/css/json-resume.css"
target = "assets/css/critical/44-json-resume.css"
[[mounts]]
# required by hugo-mod-json-resume
source = "node_modules/simple-icons/icons"
target = "assets/simple-icons"
[[mounts]]
source = "assets"
target = "assets"
[[mounts]]
source = "layouts"
target = "layouts"
[[mounts]]
source = "static"
target = "static"
[[mounts]]
source = "node_modules/prismjs"
target = "assets/prismjs"
[[mounts]]
source = "node_modules/prism-themes/themes"
target = "assets/prism-themes"
[[mounts]]
source = "node_modules/typeface-fira-code/files"
target = "static/fonts"
[[mounts]]
source = "node_modules/typeface-roboto-slab/files"
target = "static/fonts"
[[mounts]]
source = "node_modules/@tabler/icons/icons"
target = "assets/tabler-icons"

View File

@@ -0,0 +1,3 @@
category = "categories"
tag = "tags"
series = "series"

View File

@@ -0,0 +1 @@
baseURL = "https://hugo-theme-gruvbox.schnerring.net"

View File

@@ -0,0 +1,26 @@
---
title: "About"
draft: false
---
## Attributions
This theme depends on the following projects:
- [Powered by Hugo](https://gohugo.io/)
- Color scheme based on [Gruvbox](https://github.com/morhetz/gruvbox)
- Code Highlighting by [Prism](https://prismjs.com/)
- [Tabler Icons](https://tablericons.com/)
- [Simple Icons](https://simpleicons.org/)
- Content with the [#hugo-basic-example](/tags/hugo-basic-example) tag
originates from [gohugoio/hugoBasicExample](https://github.com/gohugoio/hugoBasicExample)
A big thanks to the creators! ❤️
The [profile picture of Richard Hendricks is from the Fandom Silicon Valley Wiki](https://silicon-valley.fandom.com/wiki/Richard_Hendricks?file=Richard_Hendricks.jpg).
## Legal
The theme is [licensed under MIT](https://github.com/schnerring/hugo-theme-gruvbox/blob/main/LICENSE).
All trademarks are the property of their respective owners.

View File

@@ -0,0 +1,65 @@
---
title: "Embed Video Files"
date: "2022-04-26T01:16:18+02:00"
draft: false
comments: false
socialShare: true
toc: false
---
Use the [video shortcode](https://github.com/schnerring/hugo-theme-gruvbox/blob/main/layouts/shortcodes/video.html)
to embed your video files from [Hugo Page Resources](https://gohugo.io/content-management/page-resources/).
{{< video src="my-video" autoplay="true" controls="false" loop="true" >}}
<!--more-->
With a page bundle looking like the following:
```text
embed-videos/
|-- index.md
|-- my-video.jpg
|-- my-video.mp4
|-- my-video.webm
```
You can embed `my-video` like this:
```markdown
{{</* video src="my-video" autoplay="true" controls="false" loop="true" */>}}
```
The shortcode looks for media files matching the filename `my-video*`. For each
`video` MIME type file, a `<source>` element is added. The first `image` MIME
type file is used as `poster` (thumbnail). It will render the following HTML:
```html
<video
autoplay
loop
poster="/blog/embed-videos/my-video.jpg"
width="100%"
playsinline
>
<source src="/blog/embed-videos/my-video.mp4" type="video/mp4" />
<source src="/blog/embed-videos/my-video.webm" type="video/webm" />
</video>
```
You can set a Markdown `caption`, wrapping the `<video>` inside a `<figure`>.
Additionally, the shortcode allows you to set the following attributes:
| Attribute | Default |
| ----------- | ------- |
| autoplay | `false` |
| controls | `true` |
| height | |
| loop | `false` |
| muted | `true` |
| preload | |
| width | `100%` |
| playsinline | `true` |
[Learn more about the `<video>` attributes here.](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attributes)

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@@ -0,0 +1,47 @@
+++
author = "Hugo Authors"
title = "Emoji Support"
date = "2019-03-05"
description = "Guide to emoji usage in Hugo"
tags = [
"emoji",
"hugo-basic-example",
]
+++
Emoji can be enabled in a Hugo project in a number of ways.
<!--more-->
The [`emojify`](https://gohugo.io/functions/emojify/) function can be called directly in templates or [Inline Shortcodes](https://gohugo.io/templates/shortcode-templates/#inline-shortcodes).
To enable emoji globally, set `enableEmoji` to `true` in your site's [configuration](https://gohugo.io/getting-started/configuration/) and then you can type emoji shorthand codes directly in content files; e.g.
<p><span class="nowrap"><span class="emojify">🙈</span> <code>:see_no_evil:</code></span> <span class="nowrap"><span class="emojify">🙉</span> <code>:hear_no_evil:</code></span> <span class="nowrap"><span class="emojify">🙊</span> <code>:speak_no_evil:</code></span></p>
<br>
The [Emoji cheat sheet](http://www.emoji-cheat-sheet.com/) is a useful reference for emoji shorthand codes.
***
**N.B.** The above steps enable Unicode Standard emoji characters and sequences in Hugo, however the rendering of these glyphs depends on the browser and the platform. To style the emoji you can either use a third party emoji font or a font stack; e.g.
{{< highlight html >}}
.emoji {
font-family: Apple Color Emoji, Segoe UI Emoji, NotoColorEmoji, Segoe UI Symbol, Android Emoji, EmojiSymbols;
}
{{< /highlight >}}
{{< css.inline >}}
<style>
.emojify {
font-family: Apple Color Emoji, Segoe UI Emoji, NotoColorEmoji, Segoe UI Symbol, Android Emoji, EmojiSymbols;
font-size: 2rem;
vertical-align: middle;
}
@media screen and (max-width:650px) {
.nowrap {
display: block;
margin: 25px 0;
}
}
</style>
{{< /css.inline >}}

View File

@@ -0,0 +1,82 @@
---
title: "Image Optimization"
date: "2021-10-16T23:51:37+02:00"
comments: false
socialShare: true
toc: true
cover:
src: ./alexandre-van-thuan-mr9FouttLGY-unsplash.jpg
alt: The interior of Stadsbiblioteket in Stockholm - Gunnar Asplunds library from 1928. The architecture is a transition between neoclassicism and functionalism.
caption: By [Alexandre Van Thuan](https://unsplash.com/photos/mr9FouttLGY)
---
The theme optimizes images by default with a custom [Hugo's markdown render hook](https://gohugo.io/getting-started/configuration-markup#markdown-render-hooks):
- The theme creates resized versions for each image, ranging from 100 to 700
pixels wide.
- It generates [WebP](https://en.wikipedia.org/wiki/WebP) versions for each size
if the original image format isn't WebP.
- The theme keeps the original file format as a fallback for browsers that
[don't support the WebP format](https://caniuse.com/webp).
- Images in SVG format are embedded as-is.
## Blog Post Cover Images
Use the [front matter](https://gohugo.io/content-management/front-matter/) of
your posts to add cover images:
<!-- markdownlint-disable MD013 -->
```markdown
---
cover:
src: alexandre-van-thuan-mr9FouttLGY-unsplash.jpg
alt: The interior of Stadsbiblioteket in Stockholm - Gunnar Asplunds library from 1928. The architecture is a transition between neoclassicism and functionalism.
caption: By [Alexandre Van Thuan](https://unsplash.com/photos/mr9FouttLGY)
---
```
<!-- markdownlint-enable MD013 -->
## Captions
Add captions to your inline images like this:
```markdown
---
![Alt text](image-url.jpg "Caption with **markdown support**")
---
```
![The main library in Vancouver is architecturally significant. The angles and levels contour together to produce a trippy scene. It's pretty from the outside but stunning from the inside.](aaron-thomas-dMqlE7lgyOU-unsplash.jpg "The main library in Vancouver is architecturally significant. The angles and levels contour together to produce a trippy scene. It's pretty from the outside but stunning from the inside. By [Aaron Thomas](https://unsplash.com/photos/dMqlE7lgyOU)")
## JPEG and WebP Quality
The default quality is 75%. See the [official Image Processing Config Hugo docs](https://gohugo.io/content-management/image-processing/#image-processing-config).
Change it by adding the following to the `config.toml` file:
```toml
[imaging]
quality = 75
```
## Resizing
By default, the theme creates resized versions of images ranging from 300 to 700
pixels wide in increments of 100 pixels. Override the resize behavior by
adding the following to the `config.toml` file:
```toml
[params]
[params.imageResize]
min = 300
max = 700
increment = 100
```
## Lazy Loading
Images are lazily loaded by default using the `loading="lazy"` attribute on
HTML `img` tags.
{{< video src="lazy-loading" autoplay="true" controls="false" loop="true" >}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@@ -0,0 +1,149 @@
+++
author = "Hugo Authors"
title = "Markdown Syntax Guide"
date = "2019-03-11"
description = "Sample article showcasing basic Markdown syntax and formatting for HTML elements."
tags = [
"markdown",
"css",
"html",
"hugo-basic-example",
]
categories = [
"themes",
"syntax",
]
series = ["Themes Guide"]
aliases = ["migrate-from-jekyl"]
+++
This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.
<!--more-->
## Headings
The following HTML `<h1>``<h6>` elements represent six levels of section headings. `<h1>` is the highest section level while `<h6>` is the lowest.
# H1
## H2
### H3
#### H4
##### H5
###### H6
## Paragraph
Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.
Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.
## Blockquotes
The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a `footer` or `cite` element, and optionally with in-line changes such as annotations and abbreviations.
#### Blockquote without attribution
> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
> **Note** that you can use *Markdown syntax* within a blockquote.
#### Blockquote with attribution
> Don't communicate by sharing memory, share memory by communicating.<br>
> — <cite>Rob Pike[^1]</cite>
[^1]: The above quote is excerpted from Rob Pike's [talk](https://www.youtube.com/watch?v=PAAkCSZUG1c) during Gopherfest, November 18, 2015.
## Tables
Tables aren't part of the core Markdown spec, but Hugo supports supports them out-of-the-box.
Name | Age
--------|------
Bob | 27
Alice | 23
#### Inline Markdown within tables
| Italics | Bold | Code |
| -------- | -------- | ------ |
| *italics* | **bold** | `code` |
## Code Blocks
#### Code block with backticks
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example HTML5 Document</title>
</head>
<body>
<p>Test</p>
</body>
</html>
```
#### Code block indented with four spaces
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example HTML5 Document</title>
</head>
<body>
<p>Test</p>
</body>
</html>
#### Code block with Hugo's internal highlight shortcode
{{< highlight html >}}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example HTML5 Document</title>
</head>
<body>
<p>Test</p>
</body>
</html>
{{< /highlight >}}
## List Types
#### Ordered List
1. First item
2. Second item
3. Third item
#### Unordered List
* List item
* Another item
* And another item
#### Nested list
* Fruit
* Apple
* Orange
* Banana
* Dairy
* Milk
* Cheese
## Other Elements — abbr, sub, sup, kbd, mark
<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
H<sub>2</sub>O
X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
Press <kbd><kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd></kbd> to end the session.
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.

View File

@@ -0,0 +1,51 @@
---
author: Hugo Authors
title: Math Typesetting
date: 2019-03-08
description: A brief guide to setup KaTeX
math: true
tags:
- "hugo-basic-example"
---
Mathematical notation in a Hugo project can be enabled by using third party JavaScript libraries.
<!--more-->
In this example we will be using [KaTeX](https://katex.org/)
- Create a partial under `/layouts/partials/math.html`
- Within this partial reference the [Auto-render Extension](https://katex.org/docs/autorender.html) or host these scripts locally.
- Include the partial in your templates like so:
```bash
{{ if or .Params.math .Site.Params.math }}
{{ partial "math.html" . }}
{{ end }}
```
- To enable KaTex globally set the parameter `math` to `true` in a project's configuration
- To enable KaTex on a per page basis include the parameter `math: true` in content files
**Note:** Use the online reference of [Supported TeX Functions](https://katex.org/docs/supported.html)
{{< math.inline >}}
{{ if or .Page.Params.math .Site.Params.math }}
<!-- KaTeX -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css" integrity="sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js" integrity="sha384-y23I5Q6l+B6vatafAwxRu/0oK/79VlbSz7Q9aiSZUvyWYIYsd+qj+o24G5ZU2zJz" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/contrib/auto-render.min.js" integrity="sha384-kWPLUVMOks5AQFrykwIup5lo0m3iMkkHrD0uJ4H5cjeGihAutqP0yW0J6dpFiVkI" crossorigin="anonymous" onload="renderMathInElement(document.body);"></script>
{{ end }}
{{</ math.inline >}}
### Examples
{{< math.inline >}}
<p>
Inline math: \(\varphi = \dfrac{1+\sqrt5}{2}= 1.6180339887…\)
</p>
{{</ math.inline >}}
Block math:
$$
\varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } }
$$

View File

@@ -0,0 +1,46 @@
+++
author = "Hugo Authors"
title = "Placeholder Text"
date = "2019-03-09"
description = "Lorem Ipsum Dolor Si Amet"
tags = [
"markdown",
"text",
"hugo-basic-example",
]
+++
Lorem est tota propiore conpellat pectoribus de pectora summo. <!--more-->Redit teque digerit hominumque toris verebor lumina non cervice subde tollit usus habet Arctonque, furores quas nec ferunt. Quoque montibus nunc caluere tempus inhospita parcite confusaque translucet patri vestro qui optatis lumine cognoscere flos nubis! Fronde ipsamque patulos Dryopen deorum.
1. Exierant elisi ambit vivere dedere
2. Duce pollice
3. Eris modo
4. Spargitque ferrea quos palude
Rursus nulli murmur; hastile inridet ut ab gravi sententia! Nomine potitus silentia flumen, sustinet placuit petis in dilapsa erat sunt. Atria tractus malis.
1. Comas hunc haec pietate fetum procerum dixit
2. Post torum vates letum Tiresia
3. Flumen querellas
4. Arcanaque montibus omnes
5. Quidem et
# Vagus elidunt
<svg class="canon" xmlns="http://www.w3.org/2000/svg" overflow="visible" viewBox="0 0 496 373" height="373" width="496"><g fill="none"><path stroke="#000" stroke-width=".75" d="M.599 372.348L495.263 1.206M.312.633l494.95 370.853M.312 372.633L247.643.92M248.502.92l246.76 370.566M330.828 123.869V1.134M330.396 1.134L165.104 124.515"></path><path stroke="#ED1C24" stroke-width=".75" d="M275.73 41.616h166.224v249.05H275.73zM54.478 41.616h166.225v249.052H54.478z"></path><path stroke="#000" stroke-width=".75" d="M.479.375h495v372h-495zM247.979.875v372"></path><ellipse cx="498.729" cy="177.625" rx=".75" ry="1.25"></ellipse><ellipse cx="247.229" cy="377.375" rx=".75" ry="1.25"></ellipse></g></svg>
[The Van de Graaf Canon](https://en.wikipedia.org/wiki/Canons_of_page_construction#Van_de_Graaf_canon)
## Mane refeci capiebant unda mulcebat
Victa caducifer, malo vulnere contra dicere aurato, ludit regale, voca! Retorsit colit est profanae esse virescere furit nec; iaculi matertera et visa est, viribus. Divesque creatis, tecta novat collumque vulnus est, parvas. **Faces illo pepulere** tempus adest. Tendit flamma, ab opes virum sustinet, sidus sequendo urbis.
Iubar proles corpore raptos vero auctor imperium; sed et huic: manus caeli Lelegas tu lux. Verbis obstitit intus oblectamina fixis linguisque ausus sperare Echionides cornuaque tenent clausit possit. Omnia putatur. Praeteritae refert ausus; ferebant e primus lora nutat, vici quae mea ipse. Et iter nil spectatae vulnus haerentia iuste et exercebat, sui et.
Eurytus Hector, materna ipsumque ut Politen, nec, nate, ignari, vernum cohaesit sequitur. Vel **mitis temploque** vocatus, inque alis, *oculos nomen* non silvis corpore coniunx ne displicet illa. Crescunt non unus, vidit visa quantum inmiti flumina mortis facto sic: undique a alios vincula sunt iactata abdita! Suspenderat ego fuit tendit: luna, ante urbem Propoetides **parte**.
{{< css.inline >}}
<style>
.canon { background: white; width: 100%; height: auto; }
</style>
{{< /css.inline >}}

View File

@@ -0,0 +1,243 @@
---
title: "Prism Code Highlighting Showcase"
date: "2021-07-28T04:25:37+02:00"
comments: false
socialShare: true
toc: true
---
This theme uses [Prism](https://prismjs.com/) for code highlighting. Other Hugo
themes usually include a pre-configured version of Prism, which complicates
updates and clutters the source code base with third-party JavaScript.
Only the Prism features you select in the Hugo site configuration are bundled by
the build process. This way, Prism can be easily updated with `npm` and the
size of the JavaScript and CSS bundles are minimized by only including what you
need.
<!--more-->
Here is a an example configuration demonstrating how to configure `languages`
and `plugins` in the `config.toml` file of your Hugo site:
```toml
[params]
[params.prism]
languages = [
"markup",
"css",
"clike",
"javascript"
]
plugins = [
"normalize-whitespace",
"toolbar",
"copy-to-clipboard"
]
```
## Languages
The following languages are available:
<!-- markdownlint-disable MD033 -->
<pre class="language-none" style="max-height: 500px">
<code>
{{% prism-features "languages" %}}
</code>
</pre>
<!-- markdownlint-enable MD033 -->
## Plugins
Before using a plugin in production, read its documentation and test it
thoroughly. E.g., the [`remove-initial-line-feed` plugin](https://prismjs.com/plugins/remove-initial-line-feed/)
is still available despite being deprecated in favor of [`normalize-whitespace`](https://prismjs.com/plugins/normalize-whitespace/).
Many Prism plugins require using `<pre>` tags with custom attributes. Hugo uses
Goldmark as Markdown handler, which by default doesn't render raw inline HTML,
so make sure to enable [`unsafe`](https://gohugo.io/getting-started/configuration-markup#goldmark)
rendering if required:
```toml
[markup]
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = true
```
The following plugins are available:
```none
{{% prism-features "plugins" %}}
```
### Examples
#### Copy to Clipboard
`copy-to-clipboard` requires the `toolbar` plugin, so make sure to add it
**after** adding `toolbar` in the `config.toml` file:
Config:
```toml
[params.prism]
# ...
plugins = [
"toolbar",
"copy-to-clipboard"
]
```
#### Line Numbers
Config:
```toml
[params.prism]
plugins = [
"line-numbers"
]
```
Input:
```html
<pre class="line-numbers">
<code>
Example
</code>
</pre>
```
Output:
<!-- markdownlint-disable MD033 -->
<pre class="line-numbers language-none" data-start="42">
<code>
Hello,
World!
Foo
Bar
</code>
</pre>
<!-- markdownlint-enable MD033 -->
#### Command Line
Config:
```toml
[params.prism]
languages = [
"bash"
]
plugins = [
"command-line"
]
```
Input:
```html
<pre class="command-line language-bash" data-user="root" data-host="localhost">
<code>
cd /usr/local/etc
cp php.ini php.ini.bak
vi php.ini
</code>
</pre>
<pre
class="command-line language-bash"
data-user="chris"
data-host="remotehost"
data-output="2, 4-8"
>
<code>
pwd
/usr/home/chris/bin
ls -la
total 2
drwxr-xr-x 2 chris chris 11 Jan 10 16:48 .
drwxr--r-x 45 chris chris 92 Feb 14 11:10 ..
-rwxr-xr-x 1 chris chris 444 Aug 25 2013 backup
-rwxr-xr-x 1 chris chris 642 Jan 17 14:42 deploy
</code>
</pre>
```
Output:
<!-- markdownlint-disable MD033 -->
<pre class="command-line language-bash" data-user="root" data-host="localhost">
<code>
cd /usr/local/etc
cp php.ini php.ini.bak
vi php.ini
</code>
</pre>
<pre
class="command-line language-bash"
data-user="chris"
data-host="remotehost"
data-output="2, 4-8"
>
<code>
pwd
/usr/home/chris/bin
ls -la
total 2
drwxr-xr-x 2 chris chris 11 Jan 10 16:48 .
drwxr--r-x 45 chris chris 92 Feb 14 11:10 ..
-rwxr-xr-x 1 chris chris 444 Aug 25 2013 backup
-rwxr-xr-x 1 chris chris 642 Jan 17 14:42 deploy
</code>
</pre>
<!-- markdownlint-enable MD033 -->
#### Diff Highlight
Config:
```toml
[params.prism]
languages = [
"javascript",
"diff"
]
plugins = [
"diff-highlight"
]
```
Input:
```html
<pre class="language-diff-javascript diff-highlight">
<code>
@@ -4,6 +4,5 @@
- let foo = bar.baz([1, 2, 3]);
- foo = foo + 1;
+ const foo = bar.baz([1, 2, 3]) + 1;
console.log(`foo: ${foo}`);
</code>
</pre>
```
Output:
<!-- markdownlint-disable MD033 -->
<pre class="language-diff-javascript diff-highlight">
<code>
@@ -4,6 +4,5 @@
- let foo = bar.baz([1, 2, 3]);
- foo = foo + 1;
+ const foo = bar.baz([1, 2, 3]) + 1;
console.log(`foo: ${foo}`);
</code>
</pre>
<!-- markdownlint-enable MD033 -->

View File

@@ -0,0 +1,35 @@
+++
author = "Hugo Authors"
title = "Rich Content"
date = "2019-03-10"
description = "A brief description of Hugo Shortcodes"
tags = [
"shortcodes",
"privacy",
"hugo-basic-example",
]
+++
Hugo ships with several [Built-in Shortcodes](https://gohugo.io/content-management/shortcodes/#use-hugos-built-in-shortcodes) for rich content, along with a [Privacy Config](https://gohugo.io/about/hugo-and-gdpr/) and a set of Simple Shortcodes that enable static and no-JS versions of various social media embeds.
<!--more-->
---
## YouTube Privacy Enhanced Shortcode
{{< youtube ZJthWmvUzzc >}}
<br>
---
## Twitter Simple Shortcode
{{< twitter_simple user="DesignReviewed" id="1085870671291310081" >}}
<br>
---
## Vimeo Simple Shortcode
{{< vimeo_simple 48912912 >}}

View File

@@ -0,0 +1,49 @@
---
title: "Lebenslauf"
draft: false
type: "cv"
---
## Erfahrung
{{< json-resume "work" >}}
## Ausbildung
{{< json-resume "education" >}}
## Ehrenamt
{{< json-resume "volunteer" >}}
## Auszeichnungen
{{< json-resume "awards" >}}
## Zertifikate
{{< json-resume "certificates" >}}
## Publikationen
{{< json-resume "publications" >}}
## Fähigkeiten
{{< json-resume "skills" >}}
## Sprachen
{{< json-resume "languages" >}}
## Interessen
{{< json-resume "interests" >}}
## Referenzen
{{< json-resume "references" >}}
## Projekte
{{< json-resume "projects" >}}

View File

@@ -0,0 +1,49 @@
---
title: "CV"
draft: false
type: "cv"
---
## Experience
{{< json-resume "work" >}}
## Education
{{< json-resume "education" >}}
## Volunteering
{{< json-resume "volunteer" >}}
## Awards
{{< json-resume "awards" >}}
## Certificates
{{< json-resume "certificates" >}}
## Publications
{{< json-resume "publications" >}}
## Skills
{{< json-resume "skills" >}}
## Languages
{{< json-resume "languages" >}}
## Interests
{{< json-resume "interests" >}}
## References
{{< json-resume "references" >}}
## Projects
{{< json-resume "projects" >}}

View File

@@ -0,0 +1,155 @@
{
"$schema": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json",
"basics": {
"name": "Richard Hendricks",
"label": "Programmer",
"image": "https://hugo-theme-gruvbox.schnerring.net/richard-hendricks.webp",
"email": "richard.hendricks@piedpiper.com",
"phone": "(912) 555-4321",
"summary": "Richard hails from Tulsa. He has earned degrees from the University of Oklahoma and Stanford. Before starting Pied Piper, he worked for Hooli as a part time software developer. While his work focuses on applied information theory, mostly optimizing lossless compression schema of both the length-limited and adaptive variants, his non-work interests range widely, everything from quantum computing to chaos theory.",
"location": {
"city": "San Francisco"
},
"profiles": [
{
"network": "GitHub",
"username": "hugo-theme-gruvbox",
"url": "https://github.com/schnerring/hugo-theme-gruvbox"
},
{
"network": "GitHub",
"username": "hugo-mod-json-resume",
"url": "https://github.com/schnerring/hugo-mod-json-resume"
},
{
"network": "Twitter",
"url": "https://twitter.com/SiliconHBO"
},
{
"network": "fandom",
"url": "https://silicon-valley.fandom.com/wiki/Richard_Hendricks"
},
{
"network": "HBO",
"url": "https://www.hbo.com/silicon-valley"
},
{
"network": "IMDB",
"url": "https://www.imdb.com/title/tt2575988/"
},
{
"network": "Wikipedia",
"url": "https://en.wikipedia.org/wiki/List_of_Silicon_Valley_characters#Richard_Hendricks"
}
]
},
"work": [
{
"name": "Pied Piper",
"location": "Palo Alto, CA",
"description": "Awesome compression company",
"position": "CEO/President",
"url": "http://piedpiper.com",
"startDate": "2013-12-01",
"endDate": "2014-12-01",
"summary": "Pied Piper is a multi-platform technology based on a proprietary universal compression algorithm that has consistently fielded high Weisman Scores™ that are not merely competitive, but approach the theoretical limit of lossless compression.",
"highlights": [
"Build an algorithm for artist to detect if their music was violating copy right infringement laws",
"Successfully won Techcrunch Disrupt",
"Optimized an algorithm that holds the current world record for Weisman Scores"
]
}
],
"volunteer": [
{
"organization": "CoderDojo",
"position": "Teacher",
"url": "http://coderdojo.example.com/",
"startDate": "2012-01-01",
"endDate": "2013-01-01",
"summary": "Global movement of free coding clubs for young people.",
"highlights": ["Awarded 'Teacher of the Month'"]
}
],
"education": [
{
"institution": "University of Oklahoma",
"url": "https://www.ou.edu/",
"area": "Information Technology",
"studyType": "Bachelor",
"startDate": "2011-06-01",
"endDate": "2014-01-01",
"score": "4.0",
"courses": ["DB1101 - Basic SQL", "CS2011 - Java Introduction"]
}
],
"awards": [
{
"title": "Digital Compression Pioneer Award",
"date": "2014-11-01",
"awarder": "Techcrunch",
"summary": "There is no spoon."
}
],
"publications": [
{
"name": "Video compression for 3d media",
"publisher": "Hooli",
"releaseDate": "2014-10-01",
"url": "http://en.wikipedia.org/wiki/Silicon_Valley_(TV_series)",
"summary": "Innovative middle-out compression algorithm that changes the way we store data."
}
],
"skills": [
{
"name": "Web Development",
"level": "Master",
"keywords": ["HTML", "CSS", "Javascript"]
},
{
"name": "Compression",
"level": "Master",
"keywords": ["Mpeg", "MP4", "GIF"]
}
],
"languages": [
{
"language": "English",
"fluency": "Native speaker"
}
],
"interests": [
{
"name": "Wildlife",
"keywords": ["Ferrets", "Unicorns"]
}
],
"references": [
{
"name": "Erlich Bachman",
"reference": "It is my pleasure to recommend Richard, his performance working as a consultant for Main St. Company proved that he will be a valuable addition to any company."
}
],
"projects": [
{
"name": "Miss Direction",
"description": "A mapping engine that misguides you",
"highlights": [
"Won award at AIHacks 2016",
"Built by all women team of newbie programmers",
"Using modern technologies such as GoogleMaps, Chrome Extension and Javascript"
],
"keywords": ["GoogleMaps", "Chrome Extension", "Javascript"],
"startDate": "2016-08-24",
"endDate": "2016-08-24",
"roles": ["Team lead", "Designer"],
"entity": "Smoogle",
"type": "application"
}
],
"meta": {
"canonical": "https://raw.githubusercontent.com/jsonresume/resume-schema/master/resume.json",
"version": "v1.0.0",
"lastModified": "2017-12-24T15:53:00"
}
}

View File

@@ -0,0 +1,5 @@
module github.com/davegallant/hugo-theme-gruvbox
go 1.21
require github.com/schnerring/hugo-mod-json-resume v0.0.0-20231022202951-552402b37357 // indirect

View File

@@ -0,0 +1,2 @@
github.com/schnerring/hugo-mod-json-resume v0.0.0-20231022202951-552402b37357 h1:FR5kIQJNpHrB+P6yb8PrDVO8FpbUxF5fdTXLQp7UUpI=
github.com/schnerring/hugo-mod-json-resume v0.0.0-20231022202951-552402b37357/go.mod h1:an+FdylzRESQ2N+NuFK+iLerxnDRWbOqkxCDUXflGF4=

View File

@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 135.46666 135.46667"
version="1.1"
id="svg46266"
sodipodi:docname="favicon_inkscape.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title
id="title83640">hugo-theme-gruvbox</title>
<sodipodi:namedview
id="namedview46268"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
inkscape:zoom="0.77771465"
inkscape:cx="242.37682"
inkscape:cy="297.02411"
inkscape:window-width="1278"
inkscape:window-height="1368"
inkscape:window-x="3833"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer2" />
<defs
id="defs46263">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath62400">
<rect
style="display:inline;fill:#282828;stroke-width:0.264583"
id="rect62402"
width="67.73333"
height="135.46666"
x="67.733345"
y="3.5603841e-06" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath62404">
<rect
style="fill:#282828;stroke-width:0.264583"
id="rect62406"
width="67.73333"
height="135.46666"
x="0"
y="3.5603841e-06" />
</clipPath>
</defs>
<g
inkscape:label="light"
inkscape:groupmode="layer"
id="layer1"
style="display:inline">
<rect
style="fill:#fbf1c7;stroke-width:0.264583;fill-opacity:1"
id="rect62128-5"
width="67.73333"
height="135.46666"
x="0"
y="3.5603841e-06" />
<text
xml:space="preserve"
style="font-size:158.75px;line-height:1.25;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';stroke-width:0.264583;fill:#282828;fill-opacity:1"
x="18.520834"
y="97.141769"
id="text48067"
clip-path="url(#clipPath62404)"><tspan
sodipodi:role="line"
id="tspan48065"
style="font-size:158.75px;stroke-width:0.264583;fill:#282828;fill-opacity:1"
x="18.520834"
y="97.141769">g</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="dark"
style="display:inline">
<rect
style="fill:#282828;stroke-width:0.264583"
id="rect62128-6"
width="67.73333"
height="135.46666"
x="67.733337"
y="3.5603841e-06" />
<text
xml:space="preserve"
style="font-size:158.75px;line-height:1.25;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';fill:#fbf1c7;fill-opacity:1;stroke-width:0.264583"
x="18.520834"
y="97.141769"
id="text48067-2"
clip-path="url(#clipPath62400)"><tspan
sodipodi:role="line"
id="tspan48065-4"
style="font-size:158.75px;fill:#fbf1c7;fill-opacity:1;stroke-width:0.264583"
x="18.520834"
y="97.141769">g</tspan></text>
</g>
<metadata
id="metadata83638">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>hugo-theme-gruvbox</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1 @@
<svg width="512" height="512" version="1.1" viewBox="0 0 135.47 135.47" xmlns="http://www.w3.org/2000/svg"><title>hugo-theme-gruvbox</title><defs><clipPath id="b"><rect x="67.733" y="3.5604e-6" width="67.733" height="135.47" fill="#282828" stroke-width=".26458"/></clipPath><clipPath id="a"><rect y="3.5604e-6" width="67.733" height="135.47" fill="#282828" stroke-width=".26458"/></clipPath></defs><g stroke-width=".26458"><rect y="3.5604e-6" width="67.733" height="135.47" fill="#fbf1c7"/><text x="18.520834" y="97.141769" clip-path="url(#a)" fill="#282828" font-family="'Fira Code'" font-size="158.75px" style="line-height:1.25" xml:space="preserve"><tspan x="18.520834" y="97.141769" fill="#282828" font-size="158.75px" stroke-width=".26458">g</tspan></text><rect x="67.733" y="3.5604e-6" width="67.733" height="135.47" fill="#282828"/><text x="18.520834" y="97.141769" clip-path="url(#b)" fill="#fbf1c7" font-family="'Fira Code'" font-size="158.75px" style="line-height:1.25" xml:space="preserve"><tspan x="18.520834" y="97.141769" fill="#fbf1c7" font-size="158.75px" stroke-width=".26458">g</tspan></text></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

View File

@@ -0,0 +1,8 @@
{{ define "main" }}
<article class="post">
<h1>{{ .Title | markdownify }}</h1>
<div class="post-content">
<p>This is not the page you're looking for.</p>
</div>
</article>
{{ end }}

View File

@@ -0,0 +1,4 @@
<h{{ .Level }} id="{{ .Anchor | safeURL }}">
{{- .Text | safeHTML -}}
<a href="#{{ .Anchor | safeURL }}" class="post-heading__anchor" aria-hidden="true">#</a>
</h{{ .Level }}>

View File

@@ -0,0 +1 @@
{{ partial "image.html" (dict "src" .Destination "alt" .PlainText "caption" .Title "page" .Page "lazy" true) }}

View File

@@ -0,0 +1,13 @@
{{- $isExternal := strings.HasPrefix .Destination "http" -}}
<a
href="{{ .Destination | safeURL }}"
{{ with .Title }}
title="{{ . }}"
{{ end }}
{{ if $isExternal }}
class="link--external" target="_blank" rel="noreferrer"
{{ end }}
>
{{- .Text | safeHTML -}}
</a>
{{- /* Trimm trailing whitespace */ -}}

View File

@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html
lang="{{ .Site.Language.Lang | default "en" }}"
data-theme="{{ .Param "defaultTheme" | default "light" }}"
>
{{ partial "head/head.html" . }}
<body>
<div class="layout">
{{ partial "header.html" . }}
<main>
<div class="content">
{{ block "main" . }}{{ end }}
</div>
{{ partial "sidebar.html" . }}
</main>
{{ partial "footer.html" . }}
</div>
</body>
</html>

View File

@@ -0,0 +1,9 @@
{{ define "main" }}
<div class="content-section">
<h1>{{ .Title | markdownify }}</h1>
{{ with .Content }}
<div>{{ . }}</div>
{{ end }}
</div>
{{ partial "post-list.html" .Paginator }}
{{ end }}

View File

@@ -0,0 +1,20 @@
{{ define "main" }}
<article class="post">
<div class="post-header">
<h1>{{ .Title | markdownify }}</h1>
{{ partial "post-meta.html" (dict "page" . "pageIndex" 0) }}
</div>
<div class="post-content">
{{ .Content }}
</div>
{{ if .Params.SocialShare }}
{{ partial "social-share.html" . }}
{{ end }}
{{ if .Params.Comments }}
{{ partial "comments.html" . }}
{{ end }}
</article>
{{ end }}

View File

@@ -0,0 +1,8 @@
{{ define "main" }}
<article class="post">
<h1>{{ .Title | markdownify }}</h1>
<div class="post-content">
{{ .Content }}
</div>
</article>
{{ end }}

View File

@@ -0,0 +1,12 @@
{{ define "main" }}
<div class="content-section">
<h1>{{ .Title | markdownify }}</h1>
{{ with .Content }}
<div>{{ . }}</div>
{{ end }}
</div>
<!-- See https://gohugo.io/functions/where/#mainsections -->
{{ $pages := where site.RegularPages "Type" "in" site.Params.mainSections }}
{{ $paginator := .Paginate $pages }}
{{ partial "post-list.html" $paginator }}
{{ end }}

View File

@@ -0,0 +1,9 @@
<!--
Comments at the end of posts, e.g.:
{{ template "_internal/disqus.html" . }}
... or another comment engine.
See also: https://gohugo.io/content-management/comments
-->

View File

@@ -0,0 +1,29 @@
<footer>
<div class="copyright">
{{ with $.Site.Copyright }}
{{ . | safeHTML }}
{{ end }}
</div>
</footer>
{{ $jsBuildOptions := dict "target" "es6" }}
{{ if hugo.IsProduction }}
{{ $jsBuildOptions = $jsBuildOptions | merge (dict "minify" "true") }}
{{ end }}
{{ $prism := resources.Get "js/prism.js" | resources.ExecuteAsTemplate "js/prism.js" . }}
{{ $bundle := slice $prism | resources.Concat "js/main.js" | js.Build $jsBuildOptions }}
{{ $flexsearch := resources.Get "js/flexsearch.js" | resources.ExecuteAsTemplate "js/flexsearch.js" . | js.Build $jsBuildOptions }}
{{ if hugo.IsProduction }}
{{ $bundle = $bundle | fingerprint "sha512" }}
{{ $flexsearch = $flexsearch | fingerprint "sha512" }}
{{ end }}
{{/* prettier-ignore-start */}}
<script src="{{ $bundle.RelPermalink }}"></script>
<script src="{{ $flexsearch.RelPermalink }}"></script>
{{/* prettier-ignore-end */}}
<!-- Extensibility -->
{{ partial "footer_end.html" . }}

View File

@@ -0,0 +1,3 @@
<!--
Add custom HTML at the end of <body>
-->

View File

@@ -0,0 +1,10 @@
{{ $jsBuildOptions := dict "target" "es6" }}
{{ if hugo.IsProduction }}
{{ $jsBuildOptions = $jsBuildOptions | merge (dict "minify" "true") }}
{{ end }}
{{ $darkMode := resources.Get "js/dark-mode.js" | js.Build $jsBuildOptions }}
<script>
{{ $darkMode.Content | safeJS }}
</script>

View File

@@ -0,0 +1,7 @@
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#282828" />
<meta name="msapplication-TileColor" content="#282828" />
<meta name="theme-color" content="#282828" />

View File

@@ -0,0 +1,21 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{{ partial "head/resource-hints.html" . }}
{{ partial "head/seo.html" . }}
<!-- Extensibility -->
{{ partial "head/head_start.html" . }}
{{ partial "head/stylesheets.html" . }}
{{ partial "head/dark-mode.html" . }}
{{ partial "head/favicons.html" . }}
<!-- Extensibility -->
{{ partial "head/head_end.html" . }}
</head>

View File

@@ -0,0 +1,3 @@
<!--
Add custom HTML at the end of <head>
-->

View File

@@ -0,0 +1,3 @@
<!--
Add custom HTML at the start of <head>
-->

View File

@@ -0,0 +1,42 @@
<!-- Preload commonly used font variations -->
<!-- Roboto Slab -->
<link
rel="preload"
as="font"
type="font/woff2"
href="/fonts/roboto-slab-latin-400.woff2"
crossorigin="anonymous"
/>
<!-- TODO Use prefetch when more widely supported -->
<link
rel="preload"
as="font"
type="font/woff2"
href="/fonts/roboto-slab-latin-700.woff2"
crossorigin="anonymous"
/>
<!-- Fira Code -->
<link
rel="preload"
as="font"
type="font/woff2"
href="/fonts/fira-code-latin-300.woff2"
crossorigin="anonymous"
/>
<link
rel="preload"
as="font"
type="font/woff2"
href="/fonts/fira-code-latin-400.woff2"
crossorigin="anonymous"
/>
<!-- TODO Use prefetch when more widely supported -->
<link
rel="preload"
as="font"
type="font/woff2"
href="/fonts/fira-code-latin-700.woff2"
crossorigin="anonymous"
/>

View File

@@ -0,0 +1,55 @@
{{ if eq .Kind "404" }}
<meta name="robots" content="noindex, follow" />
{{ else }}
{{ with .Params.robots }}
<meta name="robots" content="{{ . }}" />
{{ else }}
<meta
name="robots"
content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"
/>
{{ end }}
{{ end }}
{{ $title := .Title }}
{{ if .IsHome }}
{{ $title = .Site.Title }}
{{ with .Site.Params.Subtitle }}
{{ $title = printf "%s — %s" $title . }}
{{ end }}
{{ end }}
<title>{{ $title }}</title>
<meta
name="description"
content="
{{- with .Description -}}
{{- . -}}
{{- else -}}
{{- with .Summary | plainify -}}
{{- . -}}
{{- else -}}
{{- .Site.Params.description -}}
{{- end -}}
{{- end -}}"
/>
<link rel="canonical" href="{{ .Permalink }}" />
<!-- Internal Hugo templates -->
<!-- See https://gohugo.io/templates/internal -->
{{ template "_internal/twitter_cards.html" . }}
{{ template "_internal/opengraph.html" . }}
{{ template "_internal/schema.html" . }}
<!-- RSS feed -->
<!-- See https://gohugo.io/templates/rss/#reference-your-rss-feed-in-head -->
{{ range .AlternativeOutputFormats }}
<link
rel="{{ .Rel }}"
type="{{ .MediaType.Type }}"
href="{{ .RelPermalink }}"
title="{{ $.Site.Title }}"
/>
{{ end }}

View File

@@ -0,0 +1,67 @@
<!-- Styles -->
{{ $defaultTheme := .Param "defaultTheme" | default "light" }}
{{ $critical := sort (resources.Match "css/critical/*.css") "Name" | resources.Concat nil | resources.ExecuteAsTemplate nil . | resources.PostCSS }}
{{ $nonCritical := sort (resources.Match "css/non-critical/*.css") "Name" | resources.Concat "css/non-critical.css" | resources.ExecuteAsTemplate "css/non-critical.css" . | resources.PostCSS }}
{{ $prismDark := resources.Get "prism-themes/prism-gruvbox-dark.css" }}
{{ $prismLight := resources.Get "prism-themes/prism-gruvbox-light.css" }}
{{ if hugo.IsProduction }}
{{ $critical = $critical | resources.PostProcess }}
{{ $nonCritical = $nonCritical | fingerprint "sha512" | resources.PostProcess }}
{{ $prismDark = $prismDark | minify | fingerprint "sha512" }}
{{ $prismLight = $prismLight | minify | fingerprint "sha512" }}
{{ end }}
<style>
/* stylelint-disable */
{{ $critical.Content | safeCSS }}
/* stylelint-enable */
</style>
<link
rel="preload"
href="{{ $nonCritical.RelPermalink }}"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
/>
<!-- TODO Use prefetch when more widely supported -->
<link
id="prism-dark"
rel="preload"
href="{{ $prismDark.RelPermalink }}"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
{{ if eq $defaultTheme "light" }}disabled{{ end }}
/>
<!-- TODO Use prefetch when more widely supported -->
<link
id="prism-light"
rel="preload"
href="{{ $prismLight.RelPermalink }}"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
{{ if eq $defaultTheme "dark" }}disabled{{ end }}
/>
<noscript>
{{ if eq $defaultTheme "light" }}
<link
rel="stylesheet"
href="{{ $prismLight.RelPermalink }}"
/>
{{ else }}
<link
rel="stylesheet"
href="{{ $prismDark.RelPermalink }}"
/>
{{ end }}
<link
rel="stylesheet"
href="{{ $nonCritical.RelPermalink }}"
/>
</noscript>

View File

@@ -0,0 +1,62 @@
<header>
<a
class="logo"
href="
{{- with $.Site.Params.Logo.URL -}}
{{- . -}}
{{- else -}}
{{- $.Site.BaseURL -}}
{{- end -}}"
>
{{ with $.Site.Params.Logo.Text }}
<div class="logo__text">{{ . }}</div>
{{ end }}
<div class="logo__chevron">></div>
<div class="logo__cursor"></div>
</a>
<div class="search">
<input
id="search__text"
type="search"
placeholder="Search..."
aria-label="Search"
autocomplete="off"
/>
<div id="search__suggestions" class="search__suggestions--hidden"></div>
</div>
<nav id="menu">
<ul class="menu--horizontal">
{{ range $.Site.Menus.main }}
<li class="menu__item">
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
<div class="menu__burger">
<!-- Invisible checkbox indicating menu state -->
<input class="menu__item" type="checkbox" aria-label="Open main menu" />
{{ partial "icons/tabler-icon.html" "menu-2" }}
<ul class="menu--vertical">
{{ range $.Site.Menus.main }}
<li>
<a class="menu__item" href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
</nav>
<button class="theme__toggle light--hidden" aria-label="Toggle light mode">
{{ partial "icons/tabler-icon.html" "sun" }}
</button>
<button class="theme__toggle dark--hidden" aria-label="Toggle dark mode">
{{ partial "icons/tabler-icon.html" "moon" }}
</button>
</header>

View File

@@ -0,0 +1 @@
{{ partial "icons/svg.html" (printf "simple-icons/%s.svg" .) }}

View File

@@ -0,0 +1,2 @@
{{ $path := lower . }}
{{ (resources.Get $path).Content | safeHTML }}

View File

@@ -0,0 +1 @@
{{ partial "icons/svg.html" (printf "tabler-icons/%s.svg" .) }}

View File

@@ -0,0 +1,95 @@
<!-- prettier-ignore -->
{{ if .caption }}
<figure>
{{ end }}
{{ if (strings.HasPrefix .src "http") }}
<!-- External image -->
<img
src="{{ .src }}"
alt="{{ .alt }}"
{{ with .caption }}title="{{ . | markdownify | plainify }}"{{ end }}
{{ if .lazy }}loading="lazy"{{ end }}
/>
{{ else if (strings.HasPrefix .src "/") }}
<!-- Image from static/ -->
{{ $imageConfig := imageConfig (path.Join "static" .src) }}
<img
src="{{ .src }}"
alt="{{ .alt }}"
{{ with .caption }}title="{{ . | markdownify | plainify }}"{{ end }}
{{ if .lazy }}loading="lazy"{{ end }}
width="{{ $imageConfig.Width }}"
height="{{ $imageConfig.Height }}"
/>
{{ else }}
{{ $src := .src }}
{{ if strings.HasPrefix $src "./" }}
<!-- Strip "./" prefix from relative path -->
{{ $src = substr $src 2 }}
{{ end }}
<!-- Image from page bundle -->
{{ $images := .page.Resources.ByType "image" }}
{{ $image := $images.GetMatch $src }}
{{ if eq $image.MediaType.SubType "svg" }}
<!-- No processing for SVGs -->
<img src="{{ $image.RelPermalink }}" alt="{{ .alt }}" />
{{ else }}
<!-- Calculate resize widths in increments of 100px -->
{{ $minWidth := .page.Site.Params.imageResize.min | default 300 }}
{{ $maxWidth := .page.Site.Params.imageResize.max | default 700 }}
{{ $increment := .page.Site.Params.imageResize.increment | default 200 }}
{{ $widths := seq $minWidth $increment $maxWidth }}
{{ if lt $image.Width $maxWidth }}
{{ $widths = $widths | append $image.Width }}
{{ end }}
{{ $widthsCount := len $widths }}
{{ $sizes := slice }}
{{ $srcSet := slice }}
{{ $webpSrcSet := slice }}
{{ range $i, $width := $widths }}
{{ if ge $image.Width $width }}
{{ if eq $i (sub $widthsCount 1) }}
{{ $sizes = $sizes | append (printf "%dpx" $width) }}
{{ else }}
{{ $maxWidth := (add $width (sub $increment 1)) }}
{{ $sizes = $sizes | append (printf "(max-width: %dpx) %dpx" $maxWidth $width) }}
{{ end }}
{{ $resized := $image.Resize (printf "%dx" $width) }}
{{ $srcSet = $srcSet | append (printf "%s %dw" $resized.RelPermalink $resized.Width) }}
{{ if not (eq "webp" $image.MediaType.SubType) }}
<!-- If image format is not WebP, add conversions -->
{{ $webp := $image.Resize (printf "%dx webp" $width) }}
{{ $webpSrcSet = $webpSrcSet | append (printf "%s %dw" $webp.RelPermalink $webp.Width) }}
{{ end }}
{{ end }}
{{ end }}
<picture>
<source
type="image/webp"
{{ with $webpSrcSet }}srcset="{{ delimit . "," }}"{{ end }}
{{ with $sizes }}sizes="{{ delimit . "," }}"{{ end }}
/>
<img
src="{{ $image.RelPermalink }}"
{{ with $srcSet }}srcset="{{ delimit . "," }}"{{ end }}
{{ with $sizes }}sizes="{{ delimit . "," }}"{{ end }}
alt="{{ .alt }}"
{{ with .caption }}title="{{ . | markdownify | plainify }}"{{ end }}
{{ if .lazy }}loading="lazy"{{ end }}
width="{{ $image.Width }}"
height="{{ $image.Height }}"
/>
</picture>
{{ end }}
{{ end }}
<!-- prettier-ignore -->
{{ if .caption }}
<figcaption>{{ .page.RenderString .caption }}</figcaption>
</figure>
{{ end }}

View File

@@ -0,0 +1,15 @@
<div class="pagination">
{{ if .HasPrev }}
<span class="pagination__button pagination__button--previous">
<a href="{{ .Prev.URL }}">
<span><</span>
<span>Newer Posts</span>
</a>
</span>
{{ end }}
{{ if .HasNext }}
<span class="pagination__button pagination__button--next">
<a href="{{ .Next.URL }}">Older Posts &gt;</a>
</span>
{{ end }}
</div>

View File

@@ -0,0 +1,25 @@
{{ range $pageIndex, $page := .Pages }}
<article class="post">
<div class="post-header">
<h2>
<a href="{{ .RelPermalink }}">{{ .Title | markdownify }}</a>
</h2>
{{ partial "post-meta.html" (dict "page" . "pageIndex" $pageIndex) }}
</div>
<div class="post-content">
{{ with .Description }}
{{ . }}
{{ else }}
{{ .Summary }}
{{ if .Truncated }}
<div class="post-content__read-more">
<a href="{{ .RelPermalink }}">Read more &gt;</a>
</div>
{{ end }}
{{ end }}
</div>
</article>
{{ end }}
{{ partial "pagination.html" . }}

View File

@@ -0,0 +1,27 @@
<div class="post-meta">
{{- with .page.Date -}}
<span>{{ .Format "2006-01-02" }}</span>
{{- end -}}
{{- with .page.Params.Author -}}
<span> by </span><span class="post-meta__author">{{ . }}</span>
{{- end -}}
{{ with .page.Params.tags }}
<div class="post-tags">
{{ range . -}}
<a class="post-tag" href="{{ urlize (printf "tags/%s" . ) | absURL }}">
{{- /* Replace hyphen with non-breaking hyphen */ -}}
{{- replace (urlize .) "-" "&#8209;" | safeHTML -}}
</a>
{{- end }}
</div>
{{ end }}
{{ if .page.Params.cover }}
<!--
Only lazy-load images below-the-fold. For post lists, start loading
covers from the third post and onwards.
-->
{{ $lazy := ge .pageIndex 2 }}
{{ partial "image.html" (dict "src" .page.Params.cover.src "alt" .page.Params.cover.alt "caption" .page.Params.cover.caption "page" .page "lazy" $lazy) }}
{{ end }}
</div>

View File

@@ -0,0 +1,30 @@
<div class="sidebar">
{{ if .Params.toc }}
{{ with .TableOfContents }}
<aside class="toc">
<nav>
<p class="sidebar__heading">Table Of Contents</p>
{{ . }}
</nav>
</aside>
<hr />
{{ end }}
{{ end }}
{{ $cv := index $.Site.Data.json_resume $.Site.Language.Lang }}
{{ if $cv.basics }}
<aside class="bio">
{{ partial "json-resume/basics.html" . }}
{{ if $.Site.Params.tagCloud.enable }}
<hr />
{{ end }}
</aside>
{{ end }}
{{ if $.Site.Params.tagCloud.enable }}
<aside>
{{ partial "tag-cloud.html" . }}
</aside>
{{ end }}
</div>

View File

@@ -0,0 +1,20 @@
{{ $url := .Permalink }}
{{ $title := .Title }}
{{ with $.Site.Params.socialShare }}
<div class="social-share">
<strong class="social-share__heading">Share this post: </strong>
{{ range . }}
{{ $href := .formatString }}
{{ $href := replace $href "{url}" (urlquery $url) }}
{{ $href := replace $href "{title}" (urlquery $title) }}
<a
class="social-share__item"
href="{{ $href | safeURL }}"
target="_blank"
rel="noreferrer"
>
{{ partial (printf "icons/%s" .iconSuite) .iconName }}
</a>
{{ end }}
</div>
{{ end }}

View File

@@ -0,0 +1,32 @@
<!--
Original work by Artem Sidorenko: https://www.sidorenko.io/post/2017/07/nice-tagcloud-with-hugo/
Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0/)
-->
{{ with $.Site.Taxonomies.tags }}
{{ $fontUnit := "rem" }}
{{ $minFontSize := $.Site.Params.tagCloud.minFontSizeRem | default 0.8 }}
{{ $maxFontSize := $.Site.Params.tagCloud.maxFontSizeRem | default 2.0 }}
{{ $fontSizeSpread := sub $maxFontSize $minFontSize }}
{{ $min := len (index .ByCount.Reverse 0).Pages }}
{{ $max := add (len (index .ByCount 0).Pages) 1 }}
<div class="tag-cloud">
{{ range $tagName, $weightedPages := . }}
{{ $count := len $weightedPages.Pages }}
{{ $weight := div (sub (math.Log $count) (math.Log $min)) (sub (math.Log $max) (math.Log $min)) }}
{{ $fontSize := (add $minFontSize (mul $fontSizeSpread $weight) ) }}
<a
href="{{ "/tags/" | relLangURL }}{{ $tagName | urlize }}"
style="font-size:{{ $fontSize }}{{ $fontUnit }}"
{{ if eq $.Page.Params.title $tagName -}}
class="tag-cloud__tag tag-cloud__tag--active"
{{ else -}}
class="tag-cloud__tag"
{{ end -}}
>{{ $tagName }}</a
>
{{ end }}
</div>
{{ end }}

Some files were not shown because too many files have changed in this diff Show More