fix(seo): restructure head partial, fix Twitter social and SEO improvements (#749)

* fix(scss): remove top-level parent selector for Dart Sass compatibility

Dart Sass strictly disallows top-level parent selectors (`&`), which causes build failures on newer Hugo versions (e.g. 0.158.0+).

* feat(styles): refactor special styles for friends and search layouts

* fix(seo): restructure head partial, fix Twitter social and SEO improvements

- Split head template into dedicated partial hierarchy for better maintainability
- Add full Twitter Cards support with dynamic site and creator metadata
- Update social media link prefix from twitter.com to x.com and rename label to X
- Fix Twitter/X share button and Buffer via parameter handling
- Add Mastodon verification meta tags and rel="me" links
- Improve SEO structured data for both homepage and blog posts
- Add test site configuration and test avatar asset

---------

Co-authored-by: Cell <1024@lruihao.cn>
This commit is contained in:
Samir Paul
2026-05-11 14:48:45 +05:30
committed by Cell
parent 12187b0856
commit 4e0c3d8fc4
12 changed files with 87 additions and 28 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+16
View File
@@ -37,6 +37,22 @@ _merge = "shallow"
[params]
version = "0.3.X"
[params.home.profile]
enable = true
avatarURL = "/images/fixit-test.png"
[params.social]
[params.social.github]
id = "hugo-fixit"
weight = 2
[params.social.twitter]
id = "lruihao"
weight = 1
# for twitter cards
# site = "hugo-fixit"
[params.page]
collectionList = true
collectionNavigation = true
+6
View File
@@ -0,0 +1,6 @@
.special {
.single-title,
.single-subtitle {
text-align: right;
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
// Style of layout friends
@use "core/mixins" as *;
&.friends {
.special.friends {
.friend-links {
margin-top: 1rem;
display: flex;
+3 -11
View File
@@ -1,11 +1,3 @@
@use "sass:meta";
.special {
.single-title,
.single-subtitle {
text-align: right;
}
@include meta.load-css('friends');
@include meta.load-css('search');
}
@use "base.scss";
@use "friends.scss";
@use "search.scss";
+1 -1
View File
@@ -1,7 +1,7 @@
// Style of layout search
@use "core/mixins" as *;
&.search {
.special.search {
// Google CSE
.gcse-searchbox:empty,
.gcse-searchresults:empty {
+2 -2
View File
@@ -820,8 +820,8 @@ RSS = true
# [params.social.twitter]
# id = "lruihao"
# weight = 3
# prefix = "https://twitter.com/"
# Title = "Twitter"
# prefix = "https://x.com/"
# title = "X"
# [params.social.twitter.icon]
# class = "fa-brands fa-x-twitter"
+1 -1
View File
@@ -1,4 +1,4 @@
{{- hugo.Store.Set "version" "v0.4.6-20260510194406-26b0556b" -}}
{{- hugo.Store.Set "version" "v0.4.6-20260511091232-04fb47c6" -}}
{{- .Store.Set "this" dict -}}
{{- partial "init/detection-env.html" . -}}
@@ -34,10 +34,7 @@
{{- template "_internal/schema.html" . -}}
{{- template "_internal/opengraph.html" . -}}
{{- template "_internal/twitter_cards.html" . -}}
{{- with .Site.Params.social.Twitter -}}
<meta name="twitter:creator" content="@{{ . }}" />
{{- end -}}
{{- partial "layouts/head/twitter-cards.html" . -}}
<meta name="application-name" content="{{ .Site.Params.app.title | default .Site.Title }}">
<meta name="apple-mobile-web-app-title" content="{{ .Site.Params.app.title | default .Site.Title }}">
@@ -59,8 +56,8 @@
{{- $id := "" -}}
{{- $prefix := "https://mastodon.social/" -}}
{{- if reflect.IsMap . -}}
{{- $id = .Id | default .id -}}
{{- $prefix = .Prefix | default .prefix | default $prefix -}}
{{- $id = .id -}}
{{- $prefix = .prefix | default $prefix -}}
{{- end -}}
{{- if $id | and $prefix -}}
{{- $link := printf "%s/%s" $prefix $id -}}
@@ -317,3 +314,6 @@
{{- $options = dict "sourceMap" "external" | merge $options -}}
{{- end -}}
{{- dict "Source" (resources.Get "js/head/color-scheme.js") "Build" $options "Fingerprint" $fingerprint | partial "plugin/script.html" -}}
{{- /* Custom head */ -}}
{{- block "custom-head" . }}{{ end -}}
@@ -0,0 +1,40 @@
{{- /*
Extends Hugo's embedded twitter_cards partial:
https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_partials/twitter_cards.html
Key differences:
- Generates twitter:site / twitter:creator from site params (.Site.Params.social.twitter)
- The param can be a string or a map; when it's a map, reads Id/id
*/ -}}
{{- $images := partial "_funcs/get-page-images" . }}
{{- with index $images 0 }}
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="{{ .Permalink }}">
{{- else }}
<meta name="twitter:card" content="summary">
{{- end }}
{{- with or .Title site.Title site.Params.title | plainify }}
<meta name="twitter:title" content="{{ . }}">
{{- end }}
{{- with or .Description .Summary site.Params.description | plainify | htmlUnescape }}
<meta name="twitter:description" content="{{ trim . "\n\r\t " }}">
{{- end }}
{{- with site.Params.social.twitter -}}
{{- $site := "" -}}
{{- $creator := "" -}}
{{- if reflect.IsMap . -}}
{{- $creator = .creator | default .id -}}
{{- $site = .site | default $creator -}}
{{- else -}}
{{- $creator = . -}}
{{- end -}}
{{- with $site -}}
<meta name="twitter:site" content="@{{ . }}">
{{- end -}}
{{- with $creator -}}
<meta name="twitter:creator" content="@{{ . }}">
{{- end -}}
{{- end -}}
+10 -2
View File
@@ -9,9 +9,17 @@
{{- if $share.enable -}}
{{- $title := cond (.Param "capitalizeTitles") (title .Title) .Title -}}
{{- $twitterVia := "" -}}
{{- with .Site.Params.social.twitter -}}
{{- if reflect.IsMap . -}}
{{- $twitterVia = .creator | default .id -}}
{{- else -}}
{{- $twitterVia = . -}}
{{- end -}}
{{- end -}}
{{- /* 001: Twitter */ -}}
{{- if $share.Twitter -}}
<a href="javascript:void(0);" title="{{ T `shareOn` }} X" data-sharer="twitter" data-url="{{ .Permalink }}" data-title="{{ $title }}"{{ with .Site.Params.Social.Twitter }} data-via="{{ . }}"{{ end }}{{ with .Params.tags }} data-hashtags="{{ delimit . `,` }}"{{ end }}>
<a href="javascript:void(0);" title="{{ T `shareOn` }} X" data-sharer="twitter" data-url="{{ .Permalink }}" data-title="{{ $title }}"{{ with $twitterVia }} data-via="{{ . }}"{{ end }}{{ with .Params.tags }} data-hashtags="{{ delimit . `,` }}"{{ end }}>
{{- dict "Class" "fa-brands fa-x-twitter" | partial "plugin/icon.html" -}}
</a>
{{ end -}}
@@ -81,7 +89,7 @@
{{- /* 011: Buffer */ -}}
{{- if $share.Buffer -}}
<a href="javascript:void(0);" title="{{ T `shareOn` }} Buffer" data-sharer="buffer" data-url="{{ .Permalink }}" data-title="{{ $title }}"{{ with .Site.Params.Social.Twitter }} data-via="{{ . }}"{{ end }}{{ with .Params.featuredImage }} data-picture="{{ . }}"{{ end }}>
<a href="javascript:void(0);" title="{{ T `shareOn` }} Buffer" data-sharer="buffer" data-url="{{ .Permalink }}" data-title="{{ $title }}"{{ with $twitterVia }} data-via="{{ strings.TrimPrefix `@` . }}"{{ end }}{{ with .Params.featuredImage }} data-picture="{{ . }}"{{ end }}>
{{- dict "Class" "fa-brands fa-buffer" | partial "plugin/icon.html" -}}
</a>
{{ end -}}
+1 -4
View File
@@ -10,11 +10,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="robots" content="noodp" />
<title>{{ block "title" . }}{{ .Site.Title }}{{ end }}</title>
{{- partial "layouts/head.html" . -}}
{{- partial "layouts/head/index.html" . -}}
{{- /* TODO preload script https://developer.mozilla.org/zh-CN/docs/Web/HTML/Attributes/rel/preload */ -}}
{{- /* Custom head */ -}}
{{- block "custom-head" . }}{{ end -}}
</head>
<body data-instant-intensity="viewport" data-header-desktop="{{ .Site.Params.header.desktopMode }}" data-header-mobile="{{ .Site.Params.header.mobileMode }}">
{{- /* Body wrapper */ -}}