diff --git a/README.md b/README.md index 3b73cd4..14755bf 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ git submodule add https://github.com/clente/hugo-bearcub themes/hugo-bearcub To finish off, append a line to the site configuration file: ```sh -echo 'theme = "hugo-bearcub"' >> config.toml +echo 'theme = "hugo-bearcub"' >> hugo.toml ``` ## Features @@ -85,8 +85,8 @@ have to think about it again. My `_headers` file, for example, looks like this: ### Multilingual If you need to write a blog that supports more than one language, **Bear Cub** -has you covered! Check out the demo's [`config.toml` -file](https://github.com/clente/hugo-bearcub/blob/main/exampleSite/config.toml) +has you covered! Check out the demo's [`hugo.toml` +file](https://github.com/clente/hugo-bearcub/blob/main/exampleSite/hugo.toml) for a sample of how you can setup multilingual support. By default, the theme creates a translation button that gets disabled when the @@ -107,7 +107,7 @@ already implemented: matter](https://gohugo.io/content-management/front-matter/). You can also add `render: false` to your [build options](https://gohugo.io/content-management/build-options/#readout) to avoid - rendering a blank post. + rendering blank posts. - Skip link: a "skip to main content" link that is temporarily invisible, but can be focused by people who need a keyboard to navigate the web (see [PR #5](https://github.com/clente/hugo-bearcub/pull/5) by @@ -115,14 +115,21 @@ already implemented: - Reply by email: if you supply an email address, the theme creates a "Reply to this post by email" button at the end of every post (see Kev Quirk's [original implementation](https://kevquirk.com/adding-the-post-title-to-my-reply-by-email-button)). +- `absfigure` shortcode: a shortcut to use the `figure` shortcode that also + converts relative URLs into absolute URLs by using the `absURL` function. - Single-use CSS (EXPERIMENTAL): you can add some styles to a single page by writing the CSS you need in `assets/{custom_css}.css` and then including `style: "{custom_css}.css"` in the [front matter](https://gohugo.io/content-management/front-matter/) of said page. - Conditional CSS (EXPERIMENTAL): since **Bear Cub** does syntax highlighting - without inline styles (see `config.toml` for more information), it only load - its `syntax.css` if, and only if, a code block is actually present in the - current page. + without inline styles (see `hugo.toml` for more information), it only load its + `syntax.css` if, and only if, a code block is actually present in the current + page. +- Alternative "Herman" style (EXPERIMENTAL): if you want to check out a more + modern CSS style, you can change the `themeStyle` parameter to `"herman"` in + order to activate [Herman Martinus's](https://herman.bearblog.dev/) version of + the [Blogster Minimal](https://blogster-minimal.netlify.app/) theme for + [Astro](https://astro.build/). - Dynamic social card generation (EXPERIMENTAL): if you don't add preview images to a post, this template will generate one based on the title. You can see an example below. @@ -131,8 +138,8 @@ already implemented: ## Configuration -**Bear Cub** can be customized with a `config.toml` file. Check out the -[configuration](https://github.com/clente/hugo-bearcub/blob/main/exampleSite/config.toml) +**Bear Cub** can be customized with a `hugo.toml` file. Check out the +[configuration](https://github.com/clente/hugo-bearcub/blob/main/exampleSite/hugo.toml) of the [demo](https://clente.github.io/hugo-bearcub/) for more information. ```toml @@ -145,11 +152,6 @@ defaultContentLanguage = "en" # Generate a nice robots.txt for SEO enableRobotsTXT = true -# Your name. For more information on why this must be a list, see -# https://discourse.gohugo.io/t/site-author-usage/31459/8 -[author] - name = "John Doe" - # Setup syntax highlighting without inline styles. For more information about # why you'd want to avoid inline styles, see # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src#unsafe_inline_styles @@ -206,18 +208,29 @@ enableRobotsTXT = true # have the theme simply not show any link hideUntranslated = false + # (EXPERIMENTAL) This theme has two options for its CSS styles: "original" and + # "herman". The former is what you see on Bear Cub's demo (an optimized + # version of Hugo Bear Blog), while the latter has a more modern look based on + # Herman Martinus's version of the Blogster Minimal theme for Astro. + themeStyle = "original" + # (EXPERIMENTAL) This theme is capable of dynamically generating social cards # for posts that don't have `images` defined in their front matter; By setting # `generateSocialCard` to false, you can prevent this behavior. For more - # information see layouts/partials/seo_tags.html + # information see layouts/partials/social_card.html generateSocialCard = true # Social media. Delete any item you aren't using to make sure it won't show up # in your website's metadata. - [social] - email = "me@example.com" # Added to the footer so readers can reply to posts + [params.social] twitter = "example" # Twitter handle (without '@') facebook_admin = "0000000000" # Facebook Page Admin ID + + # Author metadata. This is mostly used for the RSS feed of your site, but the + # email is also added to the footer of each post + [params.author] + name = "John Doe" # Your name as shown in the RSS feed metadata + email = "me@example.com" # Added to the footer so readers can reply to posts ``` ## Contributing diff --git a/assets/herman.css b/assets/herman.css new file mode 100644 index 0000000..f2573c3 --- /dev/null +++ b/assets/herman.css @@ -0,0 +1,202 @@ +:root { + font-size: 62.5%; /* 10px */ + --color-dark: #181a20; + --color-light: #fafafa; + --color-primary: #1a8fe3; + --size: 1rem; + --spacing: calc(var(--size) * 2.4); +} + +body { + background: var(--color-dark); + color: var(--color-light); + padding: 4rem; + font-family: Avenir, 'Avenir Next LT Pro', Montserrat, Corbel, 'URW Gothic', + source-sans-pro, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol", "Noto Color Emoji"; + font-size: calc(var(--size) * 1.8); + line-height: 1.5; + min-height: 80vh; + max-width: 1600px; + margin: 0 auto; + word-wrap: break-word; +} + +header, +main, +footer { + max-width: 70ch; + margin-inline: auto; +} + +header { + padding-bottom: var(--spacing); +} + +nav a, a.blog-tags { + margin-right: calc(var(--spacing) / 2); +} +a.blog-tags { + line-height: 2; +} + +main { + padding-bottom: var(--spacing); +} + +footer { + text-align: center; + padding-top: var(--spacing); +} + +a { + color: currentColor; + text-decoration-color: var(--color-primary); + text-decoration-thickness: 0.3ex; + text-underline-offset: 0.3ex; +} + +a:hover { + text-decoration-thickness: 0.4ex; +} + +img { + display: block; + max-width: 100%; + height: auto; +} + +h1, +h2, +h3, +h4 { + font-weight: 700; + line-height: 1.3; +} + +h1 { + font-size: calc(var(--size) * 4.2); +} +h2 { + font-size: calc(var(--size) * 3.4); +} +h3 { + font-size: calc(var(--size) * 2.6); +} +h4 { + font-size: calc(var(--size) * 1.8); +} + +ul, +ol { + padding-inline-start: var(--spacing); +} +li { + margin-block-start: var(--spacing); +} + +blockquote { + padding-inline-start: var(--spacing); + border-inline-start: 0.2em solid; + font-style: italic; + max-width: 50ch; +} + +:is(h1, h2, h3, h4, blockquote) { + margin-block-end: calc(var(--spacing) / 2); +} +:is(h1, h2, h3, h4) + * { + margin-block-start: calc(var(--spacing) / 3); +} +:is(h1, h2, h3, h4) + :where(h2, h3, h4) { + margin-block-start: calc(var(--spacing) * 2); +} + +.title { + text-decoration: none; +} +.title h1 { + font-size: calc(var(--size) * 3.4); + margin-top: calc(var(--spacing) / 2); +} + +ul.blog-posts { + list-style-type: none; + padding: unset; +} +ul.blog-posts li { + display: flex; + flex-direction: column; +} +ul.blog-posts li span { + min-width: 11ch; +} + +p.byline { + opacity: 0.5; +} + +code { + font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', + Menlo, Consolas, 'DejaVu Sans Mono', monospace; + padding: 2px calc(var(--spacing) / 4); + background-color: #282a36; + font-size: calc(var(--size) * 1.4); +} +pre code { + display: block; + padding: var(--spacing); + overflow-x: auto; + -webkit-text-size-adjust: 100%; + -moz-text-size-adjust: 100%; +} + +table { + width: 100%; +} +table, +th, +td { + border: 1px solid; + border-collapse: collapse; + border-color: var(--color-light); + padding: calc(var(--spacing) / 2); +} + +.disabled { + color: currentColor; + cursor: not-allowed; + opacity: 0.5; +} + +@media screen and (min-width: 600px) { + ul.blog-posts li { + flex-direction: row; + gap: calc(var(--spacing) / 2); + } +} + +/* "Skip to main content" link */ +.skip-link { + position: absolute; + top: 5; + transform: translateY(-600%); + transition: transform 0.5s; + background-color: #181a20; + padding: 6px; +} + +.skip-link:focus { + transform: translateY(0%); +} + +figure { + margin-inline-start: 0em; + margin-inline-end: 0em; +} + +figcaption > p { + margin-block-start: 9px; + text-align: center; + font-style: italic; +} diff --git a/assets/images/social_card_bg.png b/assets/images/social_card_bg.png index ad1d528..857ea63 100644 Binary files a/assets/images/social_card_bg.png and b/assets/images/social_card_bg.png differ diff --git a/assets/images/social_card_fg.png b/assets/images/social_card_fg.png index 01b207c..e005b0a 100644 Binary files a/assets/images/social_card_fg.png and b/assets/images/social_card_fg.png differ diff --git a/assets/style.css b/assets/original.css similarity index 98% rename from assets/style.css rename to assets/original.css index 0173a36..3492237 100644 --- a/assets/style.css +++ b/assets/original.css @@ -134,6 +134,7 @@ ul.blog-posts li a:visited { a.blog-tags { line-height: 2; + margin-right: 12px; } h3.blog-filter { diff --git a/assets/syntax.css b/assets/syntax.css index 5f15788..a28d967 100644 --- a/assets/syntax.css +++ b/assets/syntax.css @@ -69,11 +69,11 @@ /* Operator */ .chroma .o { color: #ff79c6 } /* OperatorWord */ .chroma .ow { color: #ff79c6 } /* Punctuation .chroma .p { } */ -/* Comment */ .chroma .c { color: #7c90d0 } -/* CommentHashbang */ .chroma .ch { color: #7c90d0 } -/* CommentMultiline */ .chroma .cm { color: #7c90d0 } -/* CommentSingle */ .chroma .c1 { color: #7c90d0 } -/* CommentSpecial */ .chroma .cs { color: #7c90d0 } +/* Comment */ .chroma .c { color: #8491b8 } +/* CommentHashbang */ .chroma .ch { color: #8491b8 } +/* CommentMultiline */ .chroma .cm { color: #8491b8 } +/* CommentSingle */ .chroma .c1 { color: #8491b8 } +/* CommentSpecial */ .chroma .cs { color: #8491b8 } /* CommentPreproc */ .chroma .cp { color: #ff79c6 } /* CommentPreprocFile */ .chroma .cpf { color: #ff79c6 } /* Generic .chroma .g { } */ diff --git a/exampleSite/config.toml b/exampleSite/hugo.toml similarity index 82% rename from exampleSite/config.toml rename to exampleSite/hugo.toml index d5eff2b..c037e60 100644 --- a/exampleSite/config.toml +++ b/exampleSite/hugo.toml @@ -7,11 +7,6 @@ defaultContentLanguage = "en" # Generate a nice robots.txt for SEO enableRobotsTXT = true -# Your name. For more information on why this must be a list, see -# https://discourse.gohugo.io/t/site-author-usage/31459/8 -[author] - name = "John Doe" - # Setup syntax highlighting without inline styles. For more information about # why you'd want to avoid inline styles, see # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src#unsafe_inline_styles @@ -68,15 +63,26 @@ enableRobotsTXT = true # have the theme simply not show any link hideUntranslated = false + # (EXPERIMENTAL) This theme has two options for its CSS styles: "original" and + # "herman". The former is what you see on Bear Cub's demo (an optimized + # version of Hugo Bear Blog), while the latter has a more modern look based on + # Herman Martinus's version of the Blogster Minimal theme for Astro. + themeStyle = "original" + # (EXPERIMENTAL) This theme is capable of dynamically generating social cards # for posts that don't have `images` defined in their front matter; By setting # `generateSocialCard` to false, you can prevent this behavior. For more - # information see layouts/partials/seo_tags.html + # information see layouts/partials/social_card.html generateSocialCard = true # Social media. Delete any item you aren't using to make sure it won't show up # in your website's metadata. - [social] - email = "me@example.com" # Added to the footer so readers can reply to posts + [params.social] twitter = "example" # Twitter handle (without '@') facebook_admin = "0000000000" # Facebook Page Admin ID + + # Author metadata. This is mostly used for the RSS feed of your site, but the + # email is also added to the footer of each post + [params.author] + name = "John Doe" # Your name as shown in the RSS feed metadata + email = "me@example.com" # Added to the footer so readers can reply to posts diff --git a/images/social_card.webp b/images/social_card.webp index 43ae186..24bf927 100644 Binary files a/images/social_card.webp and b/images/social_card.webp differ diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 475d6f9..294749a 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -11,7 +11,7 @@ {{- partial "seo_tags.html" . -}} - {{ $style := resources.Get "style.css" | minify }} + {{ $style := print (default "original" .Site.Params.themeStyle) ".css" | resources.Get | minify }} {{ if (.Page.Store.Get "hasCodeBlock") }} diff --git a/layouts/_default/list.html b/layouts/_default/list.html index 354bc9c..e6cc177 100644 --- a/layouts/_default/list.html +++ b/layouts/_default/list.html @@ -16,7 +16,7 @@ {{ if .Params.link }} {{ .Title }} ↪ {{ else }} - {{ .Title }} + {{ .Title }} {{ end }} {{ else }} @@ -28,7 +28,7 @@ {{ if not .Data.Singular }}
{{ range .Site.Taxonomies.tags }} - #{{ .Page.Title }}   + #{{ lower .Page.Title }} {{ end }}
{{ end }} diff --git a/layouts/_default/rss.xml b/layouts/_default/rss.xml index c55e7e4..70d1193 100644 --- a/layouts/_default/rss.xml +++ b/layouts/_default/rss.xml @@ -1,42 +1,72 @@ -{{- $pctx := . -}} -{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}} -{{- $pages := slice -}} -{{- if $.IsHome -}} -{{- $pages = where $pctx.AllPages "Kind" "page" -}} -{{ else if $.IsSection }} -{{- $pages = $pctx.RegularPages -}} -{{- else -}} -{{- $pages = $pctx.Pages -}} -{{- end -}} -{{- $limit := .Site.Config.Services.RSS.Limit -}} -{{- if ge $limit 1 -}} -{{- $pages = $pages | first $limit -}} -{{- end -}} +{{- /* Deprecate site.Author.email in favor of site.Params.author.email */}} +{{- $authorEmail := "" }} +{{- with site.Params.author }} + {{- if reflect.IsMap . }} + {{- with .email }} + {{- $authorEmail = . }} + {{- end }} + {{- end }} +{{- else }} + {{- with site.Author.email }} + {{- $authorEmail = . }} + {{- warnf "The author key in site configuration is deprecated. Use params.author.email instead." }} + {{- end }} +{{- end }} + +{{- /* Deprecate site.Author.name in favor of site.Params.author.name */}} +{{- $authorName := "" }} +{{- with site.Params.author }} + {{- if reflect.IsMap . }} + {{- with .name }} + {{- $authorName = . }} + {{- end }} + {{- else }} + {{- $authorName = . }} + {{- end }} +{{- else }} + {{- with site.Author.name }} + {{- $authorName = . }} + {{- warnf "The author key in site configuration is deprecated. Use params.author.name instead." }} + {{- end }} +{{- end }} + +{{- $pctx := . }} +{{- if .IsHome }}{{ $pctx = .Site }}{{ end }} +{{- $pages := slice }} +{{- if or $.IsHome $.IsSection }} +{{- $pages = $pctx.RegularPages }} +{{- else }} +{{- $pages = $pctx.Pages }} +{{- end }} +{{- $limit := .Site.Config.Services.RSS.Limit }} +{{- if ge $limit 1 }} +{{- $pages = $pages | first $limit }} +{{- end }} {{- printf "" | safeHTML }} - {{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }} + {{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{ . }} on {{ end }}{{ .Site.Title }}{{ end }} {{ .Permalink }} - Recent content {{ if not .IsHome }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }} + Recent content {{ if not .IsHome }}{{ with .Title }}in {{ . }} {{ end }}{{ end }}on {{ .Site.Title }} Hugo -- gohugo.io - {{ site.Language.LanguageCode }}{{ with .Site.Author.email }} - {{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}{{end}}{{ with .Site.Author.email }} - {{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}{{end}}{{ with .Site.Copyright }} - {{.}}{{end}}{{ if not .Date.IsZero }} + {{ site.Language.LanguageCode }}{{ with $authorEmail }} + {{.}}{{ with $authorName }} ({{ . }}){{ end }}{{ end }}{{ with $authorEmail }} + {{ . }}{{ with $authorName }} ({{ . }}){{ end }}{{ end }}{{ with .Site.Copyright }} + {{ . }}{{ end }}{{ if not .Date.IsZero }} {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}{{ end }} - {{- with .OutputFormats.Get "RSS" -}} + {{- with .OutputFormats.Get "RSS" }} {{ printf "" .Permalink .MediaType | safeHTML }} - {{- end -}} - {{ range $pages }} + {{- end }} + {{- range $pages }} {{ .Title }} {{ .Permalink }} {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }} - {{ with .Params.author }}{{.}}{{ end }} + {{- with $authorEmail }}{{ . }}{{ with $authorName }} ({{ . }}){{ end }}{{ end }} {{ .Permalink }} - {{ .Summary | plainify }} + {{ .Summary | transform.XMLEscape | safeHTML }} {{ `` | safeHTML }} - {{ end }} + {{- end }} - \ No newline at end of file + diff --git a/layouts/_default/single.html b/layouts/_default/single.html index 89b58d5..4ff8d75 100644 --- a/layouts/_default/single.html +++ b/layouts/_default/single.html @@ -13,10 +13,10 @@

{{ range (.GetTerms "tags") }} - #{{ .LinkTitle }}   + #{{ lower .LinkTitle }} {{ end }}

-{{ with .Site.Social.email }} +{{ with .Site.Params.author.email }}

{{ i18n "email-reply" }} ↪ diff --git a/layouts/partials/header.html b/layouts/partials/header.html index 5a66907..42b5c10 100644 --- a/layouts/partials/header.html +++ b/layouts/partials/header.html @@ -1,4 +1,4 @@ -

{{ .Site.Title }}

+

{{ .Site.Title }}

diff --git a/layouts/partials/nav.html b/layouts/partials/nav.html index 5f40267..53be338 100644 --- a/layouts/partials/nav.html +++ b/layouts/partials/nav.html @@ -12,7 +12,7 @@ {{ range where .Site.Languages "Lang" "!=" .Page.Lang }} {{ with (index $translations .Lang) }} - {{ .Language.LanguageName }} + {{ .Language.LanguageName }} {{ else }} {{ if not .Params.hideUntranslated }} diff --git a/layouts/partials/social_card.html b/layouts/partials/social_card.html index 68bb4ca..d171da0 100644 --- a/layouts/partials/social_card.html +++ b/layouts/partials/social_card.html @@ -3,22 +3,33 @@ {{ $fg := resources.Get "images/social_card_fg.png"}} {{ $bg := resources.Get "images/social_card_bg.png"}} -{{ $fg = $fg.Filter (images.Text .Title (dict +{{ if gt (len .Title) 45 }} + {{ $fg = $fg.Filter (images.Text .Title (dict "font" $font - "color" "#f8f8f2" + "color" "#fafafa" + "size" 95 + "linespacing" 16 + "x" 0 + "y" 0 + )) }} +{{ else }} + {{ $fg = $fg.Filter (images.Text .Title (dict + "font" $font + "color" "#fafafa" "size" 130 "linespacing" 20 "x" 0 "y" 0 -)) }} + )) }} +{{ end }} {{ $date := .Date.Format (default "2006-01-02" .Site.Params.dateFormat) }} -{{ $author := (default $.Site.Author.name ($.Param "author") ) }} +{{ $author := (default $.Site.Params.author.name ($.Param "author") ) }} {{ $byline := (printf "%s | %s" $author $date) }} {{ $fg = $fg.Filter (images.Text $byline (dict "font" $font - "color" "#c9d1d9" + "color" "#898a8d" "size" 60 "linespacing" 30 "x" 0 @@ -29,7 +40,7 @@ {{ $card := $card.Resize "900x webp q100" }} - + @@ -63,22 +74,52 @@ {{ end }}{{ end }} {{- end }} +{{- /* Deprecate site.Social.facebook_admin in favor of site.Params.social.facebook_admin */}} +{{- $facebookAdmin := "" }} +{{- with site.Params.social }} + {{- if reflect.IsMap . }} + {{- $facebookAdmin = .facebook_admin }} + {{- end }} +{{- else }} + {{- with site.Social.facebook_admin }} + {{- $facebookAdmin = . }} + {{- warnf "The social key in site configuration is deprecated. Use params.social.facebook_admin instead." }} + {{- end }} +{{- end }} + {{- /* Facebook Page Admin ID for Domain Insights */}} -{{- with .Site.Social.facebook_admin }}{{ end }} +{{ with $facebookAdmin }}{{ end }} - -{{ with .Site.Social.twitter -}} - -{{ end -}} + +{{- /* Deprecate site.Social.twitter in favor of site.Params.social.twitter */}} +{{- $twitterSite := "" }} +{{- with site.Params.social }} + {{- if reflect.IsMap . }} + {{- $twitterSite = .twitter }} + {{- end }} +{{- else }} + {{- with site.Social.twitter }} + {{- $twitterSite = . }} + {{- warnf "The social key in site configuration is deprecated. Use params.social.twitter instead." }} + {{- end }} +{{- end }} + +{{- with $twitterSite }} + {{- $content := . }} + {{- if not (strings.HasPrefix . "@") }} + {{- $content = printf "@%v" $twitterSite }} + {{- end }} + +{{- end }} - + diff --git a/layouts/shortcodes/absfigure.html b/layouts/shortcodes/absfigure.html new file mode 100644 index 0000000..ad60c6c --- /dev/null +++ b/layouts/shortcodes/absfigure.html @@ -0,0 +1,29 @@ + + {{- if .Get "link" -}} + + {{- end -}} + {{ with .Get + {{- if .Get "link" }}{{ end -}} + {{- if or (or (.Get "title") (.Get "caption")) (.Get "attr") -}} +
+ {{ with (.Get "title") -}} +

{{ . }}

+ {{- end -}} + {{- if or (.Get "caption") (.Get "attr") -}}

+ {{- .Get "caption" | markdownify -}} + {{- with .Get "attrlink" }} + + {{- end -}} + {{- .Get "attr" | markdownify -}} + {{- if .Get "attrlink" }}{{ end }}

+ {{- end }} +
+ {{- end }} + \ No newline at end of file