Hugo SEO / Social Partials

My two cents is that I think we should include these with Hugo itself and built in partials.

They seem general enough to have broad benefits. Shipping with Hugo still enables users to either 1. Not use them, 2. Use their own, 3. Override them.

Building them in also provides a bit of structure around partials that the themes can leverage.

Remember that the way the logic is for partials. Local partials will be used if they exist, then theme partials if they exist and lastly internal ones.

We may want to have them stabilize a bit before including them, but I think that’s the direction that it should go in.

2 Likes

@spf13 I’d love to contribute them to Hugo core. To be clear, this also includes standardizing on all of the metadata locations, correct?

  • .Site.Params.authors
  • .Site.Params.social
  • .Site.Taxonomies.series
  • .Params.author //It makes coding a little more complex, but if we made this an array from the beginning, Hugo could support multiple authors out of the box.
  • .Params.social
  • .Params.images
  • .Params.videos
  • .Params.news_keywords

With that in mind, I’d also like to promote .Site.Params.Authors to be handled the same as .Site.Taxonomies, and be accessible at .Site.Authors.

so I want a discussion around this. My ideas are.

Yes on Authors. Should be an array and I like the idea of promoting it.
Site.Author can be a method that simply returns the first one making it easy to work with single author sites without much of an issue.

We should really define a set of base fields for the Author entity… and permit the user to provide their own fields as well.

I’ve been working on prototypes of automatically adding media to a page. It’s nowhere near done and probably won’t make it into v0.13 but it’s something to keep in mind.

Here’s my recommendation for the Author entity.

[Authors.jconnor]
  given_name          = "John"
  family_name         = "Connor"
  display_name        = "John Connor" // Not auto generated, just an option
  thumbnail     = "http://std3.ru/90/ff/1369441968-90ff032a2fa5dc21487fb9618977c803.jpeg"
  images        = [
                    "http://www.mirf.ru/Articles/29/5857/T2.jpg",
                    "http://img2.wikia.nocookie.net/__cb20090810001437/terminator/images/d/dc/John_conor_05.jpg",
                  ]
  short_bio     = "John Connor is the son of Sarah Connor and Kyle Reese..."
  long_bio      = "John Connor is the son of Sarah Connor and Kyle Reese, and the leader of the worldwide human resistance..."
  email         = "johnconner@gmail.com"

  [Authors.jconner.social]
    website         = "killskynet"
    github          = "killskynet"
    facebook        = "killskynet"
    twitter         = "killskynet"
    googleplus      = "killskynet"
    pinterest       = "killskynet"
    instagram       = "killskynet"
    youtube         = "killskynet"
    linkedin        = "killskynet"
    skype           = "killskynet"

This is my proposal for .Site.Social. It kind of seems like it should be promoted the same as Authors.

+++
[params.social]
github          = "nozzle"
facebook        = "nozzleio"
facebook_admin  = "17807199"  # This needs to be a page admin to get domain insights
twitter         = "nozzleio"
twitter_domain  = "nozzle.io" # This domain shows in twitter cards as "View on `twitter_domain`"
googleplus      = "NozzleIo"
pinterest       = "nozzleio"
instagram       = "nozzleio"
youtube         = "nozzleio"
linkedin        = "nozzleio"
+++

Here’s my proposal for .Params. @spf13 - I really like the idea of having methods that can be easily used to grab the first entry as a variable that can be used to access data. In fact, since we’re defining .Site.Authors, we could make a function called .AuthorDetails or something that would perform the map index to .Site.Authors and return a single author object with all the fields defined in .Site.

A similar idea would be great for images and videos, where you could immediately get access to the first one, rather than having to use the more cumbersome {{ index ... }} syntax.

I’m interested to see what your thoughts are in this regards. I was thinking somewhat the reverse, that Hugo could take embedded images and auto-add them to the end of the images array. My thought was that once we set up the standard metadata location, each individual theme could auto embed where it makes sense. The use cases that come to mind are:

  1. Featured Images - used as a thumbnail in summary views, etc. They would use the first image in the array as the featured image.
  2. Galleries - A theme could easily embed a slideshow, gallery or other grouping of media.
  3. Image preprocessing - Having arrays of data makes some other things almost trivial, like image preprocessing. The user / theme could define some standard sizes that would be autogenerated on a Hugo build. The theme could then refer to those specific sizes with some standard methods like .Params.Images[1].Large.
    +++
    // This needs to map to one of the authors setup in the .Site.Params
    authors      = [ "jconnor", "sconnor" ]

    // An array of urls to any images referenced in the post. The first image in the array will be the default shareable image.
    images       = [
                "http://www.mirf.ru/Articles/29/5857/T2.jpg",
                "http://img2.wikia.nocookie.net/__cb20090810001437/terminator/images/d/dc/John_conor_05.jpg",
              ]

    // An array of urls to any videos referenced in the post. The first video in the array will be the default shareable video.
    videos       = [
                "https://www.youtube.com/watch?v=WeON54DhMW4",
                "https://www.youtube.com/watch?v=dy5oTJajGOk",
              ]

    // The first 10 will be inserted into the Google "news_keyword" meta tag
    news_keywords = [ "skynet", "terminator", "john connor" ]
2 Likes

I have some suggestions, too, but am without desktop internet for the evening. It may be a couple of days before I can put this to virtual paper (I have some standard social icons and would use them handily).

You can see some ideas on how this works from cabaret.

What would be even better than just having image links is to also store some metadata in the front matter. If it were an array of image objects that also included things like title, caption, alt, etc, that would ease gallery creation, plus we could add those images to the sitemap like this:

<url>
	<loc>https://www.seo.com/blog/5-ways-to-spy-on-your-competition/</loc>
	<lastmod>2009-07-07T08:14:15-06:00</lastmod>
	<changefreq>weekly</changefreq>
	<priority>0.6</priority>
	<image:image>
		<image:loc>http://www.seo.com/wp-content/uploads/2009/07/secret-agent1.jpg</image:loc>
		<image:title><![CDATA[secret agent1]]></image:title>
		<image:caption><![CDATA[secret agent1]]></image:caption>
	</image:image>
	<image:image>
		<image:loc>http://www.seo.com/wp-content/uploads/2009/07/backlink.jpg</image:loc>
		<image:title><![CDATA[backlink]]></image:title>
		<image:caption><![CDATA[backlink]]></image:caption>
	</image:image>
	<image:image>
		<image:loc>http://www.seo.com/wp-content/uploads/2009/07/google-adwords-tool.jpg</image:loc>
		<image:title><![CDATA[google adwords tool]]></image:title>
		<image:caption><![CDATA[google adwords tool]]></image:caption>
	</image:image>
	<image:image>
		<image:loc>http://www.seo.com/wp-content/uploads/2009/07/seo-link-analysis.jpg</image:loc>
		<image:title><![CDATA[seo link analysis]]></image:title>
		<image:caption><![CDATA[seo link analysis]]></image:caption>
	</image:image>
</url>

I submitted an initial PR to see if I’m even heading in the right direction in terms of how things should be coded in Hugo. I still feel like I don’t know the Hugo flow at all, so feel free to correct me or point me in the right direction.

Any other feedback on these standards and/or my PR?

I think the idea of building in author as a first class type in Hugo makes sense. If that’s the case then it needs to be defined as a struct with methods. This shouldn’t be flexible. You should have a fixed set of fields. People are welcome to use what they want from it, but we can’t have on calling it “firstName” and another calling it “givenName”.

For me, I see social just as a map. I’m fine with a semi-standard list, but am all for keeping it flexible and flat.

Having it as a map with expected keys is a much better idea, I updated my PR to reflect that for Authors and also added a top level Social map. I’m not quite sure how Site and SiteInfo work throughout the system, so I added them Authors and Social to both.

Social would only need to be in site info. It’s likely authors would also only need to be there, though there could be a case to use it in Site as well.

Alrighty, I removed those from the Site struct. If there’s a reason to add Authors back, I’ll let you make that call.

Unless there are any other strong opinions, I feel like the Author & Social metadata is pretty locked down. What about standardizing media now?

I’ve finally had a chance to look at this. I have a couple of different suggestions for Author/Social metadata.

First, I’m not sure that map[string]string is right for AuthorSocial, but that could probably be (mostly) ameliorated with proper support from themes. I’ll have to think how I’d change cabaret to support this. That said, there is one case where I think that map[string](map[string]string) (if that can be represented cleanly in Go) would be better, and that’s Google+ and Facebook (and similar networks). If I’m presenting my social details in a linked list, I may not want to be listed as some.user.1632, but as my given name. In my own site, I do something like this:

[[params.social]]
  icon = "twitter"
  site = "Twitter"
  url = "https://twitter.com/halostatue"
  user = "@halostatue"

Now, out of that, icon could be automated based on the key name (and for that, I’ll suggest a different list of names, below); we can probably automate site, but maybe not—that needs to be mapped somewhere, and it needs to be mapped in an I18n-ready way, probably (that list of names). url can be automated based on the value provided, but user…can’t be guaranteed to be automated. I mean, yes, Twitter is always @<username>…but do you want to be +132342512342 if you haven’t claimed your Google+ name?

As a theme author/content producer, I need to have a way to specify that the site name may be different than the display name the user wants to show. That’s why I would prefer AuthorSocial be map[string]interface{} or map[string](map[string]string). This probably also applies to SiteSocial, but I’m not sure how that’s used (I’m still at the top of the code in review).

Now, that list of names I suggest. You put googleplus; I suggest google-plus…because it’s the name of the Font Awesome icon for Google+. (And, with a lot of these, you can also put -square after it for a squared version of the icon.) So, I have a few groups of suggested names:

Large Social Networks & Products

  • facebook, flickr, google-plus, instagram, linkedin, pinterest, qq, renren, skype, spotify, tencent-weibo, tumblr, twitter, vimeo, vine, vk, weibo, weixin, yahoo, youtube

vimeo is mildly problematic because its icon in FA is vimeo-square, and vk is short for vkontakte (the major Russian social network). tencent-weibo is different than weibo (sometimes called Sina Weibo). These are the minimum that we should probably build some support for.

Smaller Social Networks & Products

  • bitbucket, codepen, delicious, deviantart, digg, dropbox, foursquare, github, hacker-news, jsfiddle, lastfm, reddit, slack, slideshare, soundcloud, stack-exchange, stack-overflow, steam, stumbleupon, trello, twitch, xing, yelp

These are ones that we, as developers, are more likely to be interested in. Yes, it’s a bit more than just developers, but these are what I (personally) consider to be second-order networks.

Useful Things Without Icons

  • keybase, newsblur, pinboard, quora, website

At least developer useful, but these also don’t have icons in FA. In particular, I do want keybase to be on my social icons as an author, because that’s where you can find my GPG key, so I specify key as the FA icon for that one. Imperfect, but it works.

More thoughts later as I get further through this.

1 Like

I still think that @spf13’s map[string]string suggestion is the best way to store social data on the site and author level. This is a great list of social networks you’ve got here, and I think they should all be on the recommended key list.

@halostatue You’re right that the theme is going to be in charge of display and everything else related. All this PR is trying to do is to provide a unified location for storing that data, which for most intents and purposes is going to be hard coded into the appropriate places in the theme, probably something like the following:

{{ with .Author }}
  Contact me at:
  Twitter: [@{{ .Social.twitter }}](https://twitter.com/{{ .Social.twitter }})
  Facebook: [{{ .GivenName }}](https://facebook.com/{{ .Social.facebook }})
  Google+: [<i class="fa fa-google-plus-square"></i>](https://plus.google.com/{{ .Social "google-plus" }})
{{ end }}

There is very little that users should need other than their ID, which is what the map is storing per author. Any icons, internationalization or other display properties are up to the discretion of the theme developers, not Hugo.

The SiteSocial is just storing links for the business / publisher, for sitewide links to social pages.

1 Like

Would like to offer my little two cents as I’ve been doing SEO for the last 15 years.

There should be a complete separation from the Social bit and the SEO bit which should be limited to fields like

Title, meta description, author (as in meta author just to give some credit, though autorship as now gone) and canonical.

There should be also the necessity to include the alternate tag, just in case a blog post is written in more than a language and you want to support Google crawling and indexation task.

Also, in all this conversation, I didn’t see a mention about the sitemap.xml file that should be autogenerated.

@Andrea_Moro: a sitemap is automatically generated in all cases at this point unless you DisableSitemap in the configuration.

@DerekPerkins: I think that the only thing that I would want, as a theme author, is to make it easy for someone to add a social network and/or icon that I don’t know about. Your model is pretty good, but what if I want to range through .Author.Social (which is what I do now on my site’s params.social)? The list of social icons in the lower left part of the left panel (top panel if you’re on a small enough screen) at http://www.halostatue.ca/ is automatically generated from params.social; the list of contact info in my About/Where is also generated. The more I can make my theme data driven, the more flexible my theme can be for people who aren’t me.

I also think, that if at all possible, we should make it easier for someone to switch themes without having to have their own layouts overrides (one of the reasons that I put an extra.html in most of the partial directories in cabaret, and put most of the rest of the stuff in configuration/presence wrappers).

To illustrate what I mean, I’m going to include my shortcodes/socialsites.html (from my blog) and the cabaret partials/sidebar/social.html.

shortcodes/socialsites.html:

<ul>
{{ range .Page.Site.Params.social }}
  <li>
    {{ if .user }}{{ .site }} <a href="{{ .url }}">{{ .user }}</a>
    {{ else }}<a href="{{ .url }}">{{ .site }}</a>{{ end }}
  </li>
{{ end }}
</ul>

partials/sidebar/social.html from cabaret:

{{ if .Site.Params.social }}
<div class="social-buttons">
  {{ range .Site.Params.social }}
    {{ if and (isset . "url") (isset . "icon") }}
    <a href="{{ .url }}"><i class="fa lg fa-{{ .icon }}"></i></a>
    {{ end }}
  {{ end }}
</div>
{{ end }}

@halostatue I think it’s pretty standard for a theme / plugin to specifically list which social platforms they support, and in the off chance that someone wanted to support one that isn’t in the pretty extensive list you showed earlier, it would be a pretty quick feature request. You could still range through whatever social keys are set and use the same keys to map to your internal partials / definitions, generating your same lists on your site.

  1. It’s the easiest for people to configure by just adding their username / ID / handle
  2. Every theme is going to have different ways of displaying the data, and that support should live in the theme. I’m not advocating user layout overrides, for the same reasons you mentioned.

I think I know what I would actually want here, and I’m not currently sure how to do it. I almost want to be able to have a config.toml (or something like that) that provides theme configuration for stuff like this, that provides default behaviour and a good example of how to perform extra configuration for something like this. (That way, I can write cabaret to know how to deal with a fairly large set, but make it easy for a user to add another set in the site config.toml).

I need to think about how this would work and propose it a bit more formally. If we have that capability, then I’m pretty OK with this as written for Author and Social. (I’ll look at media soon.)