diff --git a/.github/workflows/hugo.yml b/.github/workflows/hugo.yml new file mode 100644 index 0000000..574df52 --- /dev/null +++ b/.github/workflows/hugo.yml @@ -0,0 +1,80 @@ +# Sample workflow for building and deploying a Hugo site to GitHub Pages +name: Deploy Hugo exampleSite to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: + - main + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +# Default to bash +defaults: + run: + shell: bash + +jobs: + # Build job + build: + runs-on: ubuntu-latest + env: + HUGO_VERSION: 0.142.0 + steps: + - name: Install Hugo CLI + run: | + wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ + && sudo dpkg -i ${{ runner.temp }}/hugo.deb + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + fetch-depth: 0 + - name: Setup Pages + id: pages + uses: actions/configure-pages@v3 + - name: Install Node.js dependencies + run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true" + - name: Build with Hugo + env: + # For maximum backward compatibility with Hugo modules + HUGO_ENVIRONMENT: production + HUGO_ENV: production + run: | + hugo \ + --gc \ + --minify \ + --themesDir ../.. \ + --source exampleSite \ + --baseURL "${{ steps.pages.outputs.base_url }}/" + - name: Index pagefind + run: npx pagefind --source "./exampleSite/public" + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: ./exampleSite/public + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e7e3002 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.hugo_build.lock +public diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a8bddb7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 foxihd @ github.com + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba679c4 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# Hugo Brewm + +> This is a fine-brewed Hugo theme I made for my personal blog. +> I'm happy to share this theme with the community. +> +> With joy xx, +> — [foxx](https://github.com/foxihd) + +Demosite: [https://hugo.brewm.co](https://hugo.brewm.co) + +## Feature Highlights + +- **Reader-first**: Prioritizes readability and accessibility with personalized settings for colors, fonts, and BionRead mode. +- **Inclusive**: Graceful degradation design oriented with improved semantic HTML structure & WAI-ARIA attribute. +- **Scalable**: Support for multiple authors and languages. +- **Extensible**: Integrated Pagefind search functionality, external feed over RSS and embed decentralized comments through Fediverse. +- **No-framework**: Lower maintenace & carbon footprint by lesser resource usage. + +## Installation + +1. Create a new Hugo site (for an existing hugo site, skip to step 2) : + +``` +hugo new site mysite +cd mysite +git init +``` + +2. Add this theme as a Git submodule: + +``` +git submodule add https://github.com/foxihd/hugo-brewm themes/hugo-brewm +``` + +3. Update your site's configuration in `config.toml`: + +``` +theme = "hugo-brewm" +``` + +# Special Thanks + +This project could not be made, without a lot efforts of — thank to: + +- [Aliftype/amiri](https://github.com/aliftype/amiri) - for Amiri. +- [Alvarotrigo on Codepen](https://codepen.io/alvarotrigo/pen/rNbxNWg) - for Logotype. +- [Antijingoist/opendyslexic/](https://github.com/antijingoist/opendyslexic/) - for OpenDyslexic typeface. +- [Datalog/qrcode-svg](https://github.com/datalog/qrcode-svg) - for page QR code generation. +- [Dpecos/mastodon-comments](https://github.com/dpecos/mastodon-comments) - for Mastodon comments. +- [Georgd/EB-Garamond](https://github.com/georgd/EB-Garamond), [Imedadel/typeface-eb-garamond-latest/](https://github.com/imedadel/typeface-eb-garamond-latest/) & [Googlefonts/ebgaramond-specimen/](https://github.com/googlefonts/ebgaramond-specimen/) - for serif typeface. +- [GoogleFonts/Inconsolata](https://github.com/googlefonts/Inconsolata) - for teletype typeface. +- [IcoMoon](https://icomoon.io) - for icon font. +- [JulietaUla/Montserrat](https://github.com/JulietaUla/Montserrat) - for sans-serif typeface. +- [Markmead/JS Bionic Reading](https://github.com/markmead/js-bionic-reading) - for BionRead support. +- [Msurguy/Flow Lines](https://github.com/msurguy/flow-lines) - for generated feature images. +- [Omnibus-Type/Rosario](https://github.com/Omnibus-Type/Rosario) - for sans-serif typeface. +- [Risilab/Cormorant](https://github.com/risilab/cormorant) - for serif typeface. +- [Rsms/Inter](https://github.com/rsms/inter) - for sans-serif typeface. +- [Skoch/Crimson](https://github.com/skosch/Crimson) - for serif typeface. +- [Slashformotion/Hugo Tufte](https://github.com/slashformotion/hugo-tufte) - for figure & marginpar shortcodes. + +## License + +This theme is released under the MIT License. diff --git a/archetypes/default.md b/archetypes/default.md new file mode 100644 index 0000000..c6f3fce --- /dev/null +++ b/archetypes/default.md @@ -0,0 +1,5 @@ ++++ +title = '{{ replace .File.ContentBaseName "-" " " | title }}' +date = {{ .Date }} +draft = true ++++ diff --git a/assets/css/component/a11y.css b/assets/css/component/a11y.css new file mode 100644 index 0000000..858558a --- /dev/null +++ b/assets/css/component/a11y.css @@ -0,0 +1,357 @@ +/* a11y button */ + +#has-a11y { + margin-right: auto; +} + +#has-a11y-summary { + padding: .6ex 1ex; + font-weight: 700; +} + +#has-a11y-summary::before{ + font-family: 'base-ui'; + font-weight: 400; + content: '\e900'; +} + +#has-a11y[open] #a11y { + --anm: expand 99ms forwards; + --tso: bottom; +} + +/* a11y container */ +#a11y { + display: flex; + position: fixed; + bottom: calc(1rem + var(--vfoot)); + flex-direction: column; + margin: 0; + border: var(--border); + border-radius: 1ex; + background: var(--bg); + padding: 1rem; + max-height: calc(100vh - var(--vhead) - var(--vfoot) - 2rem ); + overflow-y: auto; + gap: var(--medskip); +} + +/* hide input checkbox */ +#a11y input[type=checkbox], +#a11y input[type=radio], +#bionReadSwitch, +.marginpar-ctrl { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + position: absolute; + top: -100vh; +} + +#a11y select, +#a11y input[type=range], +#setColorScheme label, +#setOpenDyslexic, +button { + cursor: pointer; +} + +/* range */ +label.range { + display: flex; + flex-direction: row-reverse; +} + +label.range > input { + flex: 1; + accent-color: var(--ac); +} + +label.range > output { + width: 4rem; + text-align: center; + color: var(--ac); + font: 700 var(--large) var(--sf), sans-serif; +} + +#setColorScheme { + display: flex; + flex-direction: row; + align-items: center; + margin-bottom: 1ex; +} + +/* lightswitch */ +#lightSwitch + label { + display: flex; + flex-direction: column; + align-items: center; + border-radius: 0.5ex; + padding: 1ex; + width: 4rem; + gap: 1ex; +} + +#lightSwitch:hover + label, +#lightSwitch:focus + label, +#setContrast > input:hover + label { + background: var(--g18s); + color: var(--ac); +} + +/* credit to @mrozilla on codepen https://codepen.io/mrozilla/pen/OJJNjRb */ +#lightSwitchIndicator { + --size: 1.414em; + display: block; + outline: none; + border-radius: 999px; + box-shadow: inset calc(var(--size) * 0.33) calc(var(--size) * -0.25) 0; + width: var(--size); + height: var(--size); + color: #ffaa00; +} + +input:checked + label > #lightSwitchIndicator { + --ray-size: calc(var(--size) * -0.4); + --offset-orthogonal: calc(var(--size) * 0.65); + --offset-diagonal: calc(var(--size) * 0.45); + --tsf: scale(0.75); + box-shadow: inset 0 0 0 var(--size), + calc(var(--offset-orthogonal) * -1) 0 0 var(--ray-size), + var(--offset-orthogonal) 0 0 var(--ray-size), + 0 calc(var(--offset-orthogonal) * -1) 0 var(--ray-size), + 0 var(--offset-orthogonal) 0 var(--ray-size), + calc(var(--offset-diagonal) * -1) calc(var(--offset-diagonal) * -1) 0 var(--ray-size), + var(--offset-diagonal) var(--offset-diagonal) 0 var(--ray-size), + calc(var(--offset-diagonal) * -1) var(--offset-diagonal) 0 var(--ray-size), + var(--offset-diagonal) calc(var(--offset-diagonal) * -1) 0 var(--ray-size); +} + +/* contrast */ +#setContrast { + display: flex; + position: relative; + flex: 1; + justify-content: center; + margin: 0 0 0 1ex; + border: none; + border-left: var(--bound); + padding: 2.5em 0 0 1ex; +} + +#setContrast legend { + position: absolute; + top: 0; + padding: 1ex; +} + +#setContrast label { + -webkit-transition: 99ms; + -moz-transition: 99ms; + -o-transition: 99ms; + transition: 99ms; + border-radius: 1rem; + padding: 3pt 7pt; + font-size: 0.84em !important; +} + +#setContrast > input:checked + label { + background: var(--fg); + color: var(--bg); +} + +#setContrast > input + label::before { + font-family:'base-ui'; + content: '\e904\a0'; +} + +#setContrast > input:checked + label::before { + content: '\e903\a0'; +} + +/* color palette */ +#setColorPalette { + align-items: baseline; +} + +#setColorPalette > *, +#setOpenDyslexic > * { + padding: 1ex 0; +} + +#colorPalette { + flex: 1; + margin-left: 0.5ex; + border: unset; + border-radius: 1rem; + background: var(--bg); + padding: 0.5ex 1.25ex; + color: var(--fg); + font: inherit !important; +} + +/* font */ +#baselineStretchState::after { + font-size: 0.8em; + content: '×'; +} + +#fontSizeState::after { + font-size: 0.8em; + content: 'pt'; +} + +#setOpenDyslexic > label { + display: flex; + align-items: center; +} + +#OpenDyslexicState { + display: flex; + margin-left: auto; + border-radius: 1em; + background: #80808080; + width: 2.4em; + height: 0.8em; +} + +#OpenDyslexicIndicator { + --tst: 0.5s ease-out; + position: relative; + align-self: center; + border: 1pt solid #80808008; + border-radius: 1em; + box-shadow: var(--box-shadow-focus); + background: #fff; + padding: 6pt; +} + +#OpenDyslexic:hover + label #OpenDyslexicIndicator, +#OpenDyslexic:focus + label #OpenDyslexicIndicator { + --anm: grab 3s ease-out 3; +} + +#OpenDyslexic:checked:hover + label #OpenDyslexicIndicator, +#OpenDyslexic:checked:focus + label #OpenDyslexicIndicator { + --anm: unset; +} + +#OpenDyslexic:checked + label #OpenDyslexicIndicator { + left: 1.3em; +} + +@keyframes grab { + 0%, 10%, 20%, 100% { + left: 0; + } + 5%, 15% { + left: 3pt; + } +} + +@-webkit-keyframes grab { + 0%, 20%, 40%, 100% { + left: 0; + } + 10%, 30% { + left: 3pt; + } +} + +/* menu */ +#a11y-menu { + display: inline-flex; + align-items: center; + margin: 0; + padding: 0; + width: 100%; + gap: 1ex; +} + +#a11y-menu > button { + display: inline-flex; + flex-direction: column; + align-items: center; + border: unset; + padding: 1ex; +} + +#a11y-menu > .has-aria-label { + background: unset; + color: var(--fg); +} + +#a11y-menu > .has-aria-label::before { + margin: auto; + padding: 0.25rem 0.5rem; +} + +#a11y-menu .has-aria-label:after { + font-size: 0.7em; +} + +#resetButton::before { + font-family: 'base-ui'; + content: '\e90f'; +} + +#closeButton::before { + font-family: 'base-ui'; + content: '\e913'; +} + +#bionReadButton, +#saveButton { + flex: 1; + margin-left: auto; + cursor: pointer; +} + +/* bionRead */ +b.k { + vertical-align: baseline; + letter-spacing: var(--bion); + color: var(--off); + font-weight: 400 !important; + -webkit-text-stroke: var(--bion) var(--off); + font-synthesis: weight; +} + +#useBionRead { + display: none; + margin: auto; + letter-spacing: 0.1em; + font-size: var(--footnotesize); +} + +legend, +.has-aria-label-top:before, +#setColorPalette > label, +input + label > span { + font-weight: 700; +} + +#setContrast > input + label > span { + font-weight: 400; +} + +#bionReadButton > span { + font-weight: 400; + text-transform: uppercase; +} + +#noScript, +#noLocalStorage { + margin: auto; + border-top: var(--bound); + padding-top: 1rem; + max-width: 20rem; +} + +@media only screen and (max-width: 640px) { + + #has-a11y > summary > span { + display: none; + } + +} \ No newline at end of file diff --git a/assets/css/component/background.css b/assets/css/component/background.css new file mode 100644 index 0000000..604f7b2 --- /dev/null +++ b/assets/css/component/background.css @@ -0,0 +1,98 @@ +#background-footer, +#background-header, +#background-body { + display: flex; + position: fixed; + top: 0; + left: 0; + z-index: -2; + width: 100vw; + height: 100vh; +} + +#background-footer, +#background-header, +#background-body, +#a11y, +#lightSwitchIndicator, +#setContrast, +#colorPalette, +ul.carousel__viewport > li > a > span::before, +ul.carousel__viewport > li > a > span::after, +ul.carousel__viewport > li > a > span, +#list-categories a:hover::after, +.letterine > i { + -webkit-transition: var(--flashGuard); + -moz-transition: var(--flashGuard); + -o-transition: var(--flashGuard); + transition: var(--flashGuard); +} + +#background-body, +.background { + background-color: var(--bg); +} + +#background-header { + /* border-bottom: var(--border); */ + height: var(--vhead); +} + +#background-footer { + top: unset; + bottom: 0; + /* border-top: var(--border); */ + height: var(--vfoot); +} + +/* grain */ + +.grain, +#grain { + background-image: url("data:image/svg+xml;utf8,"); + mix-blend-mode: difference; +} + +#grain { + position: absolute; + width: 100%; + height: 100%; +} + +/* distraction */ + +#dwclock { + opacity: 0.33; + margin: auto; + width: 100vmin; + height: 100vmin; + filter: blur(2vmin) saturate(2); +} + +#hour, +#min { + position: absolute; + width: 100vmin; + height: 100vmin; +} + +.hand { + --min: 40vmin; + --hour: 28vmin; + --tsf: translateY(calc(50vmin - var(--min))); + margin: 0 auto auto ; + border-right: 2vmin solid transparent; + border-bottom: var(--min) solid #60f; + border-left: 2vmin solid transparent; + border-radius: 2vmin; + background-image: linear-gradient(0deg, var(--bg) 0%, #60f 100%); + width: 3vmin; + height: var(--min); +} + +#hour .hand { + --tsf: translateY(calc(50vmin - var(--hour))); + border-bottom: var(--hour) solid #20f; + background-image: linear-gradient(0deg, var(--bg) 0%, #20f 100%); + height: var(--hour); +} \ No newline at end of file diff --git a/assets/css/component/breadcrumb.css b/assets/css/component/breadcrumb.css new file mode 100644 index 0000000..c6924c8 --- /dev/null +++ b/assets/css/component/breadcrumb.css @@ -0,0 +1,115 @@ +main > header.pagewidth { + margin-top: var(--medskip); + padding-left: calc(var(--void) - 0.5ex - 4pt); + height: 2.5rem; +} + +#back { + display: inline-flex; + position: relative; + top: 2.8pt; + float: left; + border: none; + background: none; + padding: 0; + color: inherit; +} + +#back::before, +header > menu a::before { + padding: 6pt 9pt; + font-size: var(--large); +} + +#rss-button::before { + font-family: 'base-ui'; + content: '\e910'; +} + +#print-button::before { + font-family: 'base-ui'; + content: '\e90c'; +} + +#navigatorShare::before { + font-family: 'base-ui'; + content: '\e912'; +} + +#copyPermalink::before { + font-family: base-ui; + content: '\e905'; +} + +main > header > menu { + float: right; + visibility: hidden; + margin: 0 0 0 auto; + padding: 0; + list-style: none; + font-size: 1.1rem; +} + +main#term > header > menu, +main#page > header > menu { + visibility: visible; +} + +main#term #print-button { + visibility: hidden; +} + +main > header > menu > li, +ul.breadcrumb > li { + display: inline; +} + +main > header > menu > li > a { + opacity: 0.86; + color: var(--fg) !important; +} + +.pagination .rfill a::before, +.carousel__viewport__slide:last-child > nav a::before, +#back::before { + --tsf: rotate(-90deg) translateX(1pt); + font-family: base-ui; + content: '\e902'; +} + +#has-breadcrumb a { + margin: 2pt; + padding: 0.5ex; + text-transform: uppercase; + letter-spacing: 0.1rem; + color: var(--fg); + font-size: var(--small); +} + +ul.breadcrumb { + display: flex; + padding: 3pt 0; + overflow-y: auto; + text-overflow: ellipsis; + white-space: nowrap; +} + +ul.breadcrumb > li > a, +header > menu a { + display: inline-flex; + position: relative; +} + +ul.breadcrumb > li:last-child a { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-touch-callout: none; +} + +ul.breadcrumb > li + li:before { + color: var(--mid); + content: '/'; +} \ No newline at end of file diff --git a/assets/css/component/card.css b/assets/css/component/card.css new file mode 100644 index 0000000..37e6f7e --- /dev/null +++ b/assets/css/component/card.css @@ -0,0 +1,163 @@ +.feed-item { + position: relative; + transition: 99ms; + margin: var(--medskip) auto var(--medskip) 0; + padding: 1rem; + width: var(--golden-ratio); + font-size: var(--small); +} + +.feed-item:hover, +.feed-item:focus-within { + --tsf: scale(1.01); +} + +.feed-item * { + margin-top: 0; + margin-bottom: 0; +} + +.feed-item h1 { + margin: 8pt 0 0 0; + font-size: var(--Large); + font-weight: 700; +} + +.feed-item img { + width: auto; + height: auto; + max-height: 38vh; + aspect-ratio: 10/8; + object-fit: cover; +} + +.feed-item .par { + --grd: linear-gradient(var(--off) 50%, transparent 100%); + opacity: 0.86; + margin-right: auto; + -webkit-mask-image: var(--grd); + mask-image: var(--grd); + clip-path: text; +} + +.feed-item img + .par { + width: var(--golden-ratio); +} + +.feed-item > a { + position: absolute; + top: 2.5rem; + right: 0; + bottom: 3rem; + left: 0; + transition: 0.2s; + z-index: 1; +} + +.feed-item > a:hover, +.feed-item > a:focus { + top: 0; + bottom: 0; + background-color: #80808008; +} + +.feed-item > hgroup { + display: flex; + flex-direction: column; +} + +.feed-item > section { + display: flex; + flex-direction: row-reverse; + margin: 8pt 0; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + aspect-ratio: 10/3; + gap: 1rem; +} + +.feed-item > footer, +.feed-item > hgroup > div { + display: flex; + flex-flow: nowrap; + align-items: center; + white-space: nowrap; +} + +.feed-item > footer > ul, +.feed-item > hgroup > div > span { + display: block; + overflow-x: auto; + -ms-overflow-style: none; + scrollbar-width: none; +} + +.feed-item > hgroup > div > div { + margin-left: auto; + padding-left: 1rem; +} + +.feed-item > footer > ul { + text-align: right; + font-size: var(--scriptsize); +} + +.readingTime { + margin-right: auto; + padding-right: 1rem; +} + +.readingTime + .baselineskip { + height: 1em; +} + +.flowlines { + border: var(--border); + object-fit: none !important; +} + +ul.pagination { + display: flex; + margin: var(--medskip) 1ex; +} + +.pagination a { + display: flex; + padding: 1ex 1em; + text-transform: uppercase; + letter-spacing: 0.2ex; + color: var(--ac); + gap: 1ex; +} + +@media only screen and (max-width: 960px) { + + .feed-item { + width: unset; + } + + aside + #list-posts .feed-item { + width: var(--golden-ratio); + } + + aside + #list-posts .feed-item > section { + flex-direction: column; + aspect-ratio: unset; + } + + aside + #list-posts .feed-item .par { + max-height: 25vh; + width: unset; + } + +} + +@media only screen and (max-width: 480px) { + + .feed-item > section { + flex-direction: column; + aspect-ratio: unset; + } + +} \ No newline at end of file diff --git a/assets/css/component/carousel.css b/assets/css/component/carousel.css new file mode 100644 index 0000000..e0690a1 --- /dev/null +++ b/assets/css/component/carousel.css @@ -0,0 +1,293 @@ +/* + +