11ty / eleventy

A simpler site generator. Transforms a directory of templates (of varying types) into HTML.
https://www.11ty.dev/
MIT License
17.21k stars 493 forks source link

plugin: SEO #417

Closed DirtyF closed 5 years ago

DirtyF commented 5 years ago

11ty-plugin-seo helps add necessary markup for SEO.

/cc @chrisdmacrae

Ryuno-Ki commented 5 years ago

Would it be possible to break this down into smaller plugins as well? For example, what if I would like to have JSON-LD, but not Twitter Cards?

I would imagine that the SEO plugin loads those smaller plugins and expose them under a single API. Maybe a lerna repo?

AndrewAsquith commented 5 years ago

Are there any thoughts on how this should work?

My approach is currently a collection of shortocdes, so you'd end up with something like this that you could mix and match as desired:

{% metaTitle pageTitle %}
{% metaDescription pageDescription %}
{% canonical absolutePageUrl %}

{% twitterSummaryCard author.twitter.name, pageTitle, pageDescription, pageImage %}

{% opengraphBasic pageTitle, ogpType or 'website', site.url, pageImage or site.logo %}

I'm not sure how well that would work in a generic form for json-ld since it's basically kvps you can use in any number of ways.

My current implementation of the above is here: https://github.com/AndrewAsquith/andrewasquith-ca/tree/master/_11ty/shortcodes

Ryuno-Ki commented 5 years ago

Yeah, my idea would have been to write seo as a collection of shortcodes (so everybody is free to mix and match as they please).

Then one additional shortcode {% seo %} which uses the single shortcodes under the hood.

The information should be placed in a JSON under _data/, so it is available from everywhere.

AndrewAsquith commented 5 years ago

So I've been playing with my implementation and ended up settling on this for the moment:

https://github.com/AndrewAsquith/andrewasquith-ca/blob/master/src/site/_includes/components/meta.njk

Does it make sense to pull my shortcodes out into a plugin? And how far to take that? I left some generic shortcodes in rather than implementing every possibility. And while I have a quick check to try and ensure the JSON is well formed, it's probably still possible to end up with invalid markup. I'd rather not be responsible for a project that sullies 11ty's name. ;)

Any other ideas or better implementations?

@chrisdmacrae I couldn't find anything in your public repos, but that doesn't mean it's not hiding until it's ready.

@zachleat do you have any suggestions/feedback ?

illvart commented 5 years ago

I set SEO manually.

My project is still localhost, I haven't released it yet.

Example like this:

{# meta SEO #}
{% include "meta-seo.njk" %}

The meta-seo.njk can be found here.

Ryuno-Ki commented 5 years ago

I'm currently playing around with microformats2 h-event and its JSON-LD pendant.

Currently not quite happy with the structure of my event.njk partial:

<div class="h-event vevent">
  <h3>
    <a class="p-name summary u-url url" href="{{ event.url }}">{{ event.name }}</a>
  </h3>
  <p>
    From
    <span class="dt-start dtstart" datetime="{{ event.startDate }}">
      <abbr class="value" title="{{ event.startDate }}">
        {{ event.startDay }}.{{ event.startMonth }}.{{ event.startYear }}
      </abbr>
    </span>
    to
    <span class="dt-end dtend" datetime="{{ event.endDate }}">
      <abbr class="value" title="{{ event.endDate }}">
        {{ event.endDay }}.{{ event.endMonth }}.{{ event.endYear }}
      </abbr>
    </span>
    {% if event.location %}
      at <span class="p-location location">
        {% set location = event.location %}
        {% include "_partials/location.njk" %}
      </span>
    {% endif %}
  </p>
</div>

<script type="application/ld+json">
{
  "@context": "http://schema.org/",
  "@type": "Event",
  "name": "{{ event.name }}",
  "url": "{{ event.url }}",
  "startDate": "{{ event.startDate }}",
  "endDate": "{{ event.endDate }}"{% if event.location %},
  "location": {
    "@type": "Place",
    "name": "{{ event.location.name }}",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "{{ event.location.address }}",
      "postalCode": "{{ event.location.postalCode }}",
      "addressLocality": "{{ event.location.locality }}",
      "addressCountry": "{{ event.location.country }}"
    }
  }
  {% endif %}
}
</script>

I keep my events in a Data File for now. Maybe switching to a collection later on.

danfascia commented 5 years ago

Right now I solve this without a plugin using the {% block %} inheritance features.

In your main base template you should have a basic SEO block. Here is a reasonable starting point

   <title>
        {%- if title -%}
            {{title}} - {{site.title}}
        {%- elseif renderData.title -%}
            {{renderData.title}} - {{site.title}}
        {%-else-%}
            {{site.title}} {{site.strapline}}
        {%- endif -%}
    </title>
    <meta name="description" content="{% block description %}{{site.description}}{% endblock %}">
    <meta name="robots" content="index,follow">
    <meta name="generator" content="Eleventy">
    <meta name="subject" content="Your site subject">

    {%- block seo -%}
    <!--Twitter Card-->
    <meta name="twitter:card" content="summary">
    <meta name="twitter:site" content="@{{site.twitter}}">
    <meta name="twitter:creator" content="@{{site.twitter}}">
    <meta name="twitter:url" content="{{site.baseURL}}{{page.url}}">
    <meta name="twitter:title" content="{{title}}">
    <meta name="twitter:description" content="{{ site.description }}">

    <!--Schema-->
    <link rel="author" href="{{site.baseURL}}">
    <link rel="publisher" href="{{site.baseURL}}">
    <meta itemprop="name" content="{{title}}">
    <meta itemprop="description" content="{{ site.description }}">

    <!-- Facebook OpenGraph -->
    <meta property="fb:app_id" content="{{site.FB_APP_ID}}">
    <meta property="og:url" content="{{site.baseURL}}{{page.url}}">
    <meta property="og:type" content="website">
    <meta property="og:title" content="{{title}}">
    <meta property="og:description" content="{{ site.description }}">
    <meta property="og:site_name" content="{{site.title}}">
    <meta property="og:locale" content="en_GB">
    <meta property="article:author" content="{{site.baseURL}}">
    {%- endblock -%}

Next, within each inherited template you have the option of including a more page specific {% block seo %} which will override the values in the base template.

Here is an example

{%- block seo -%}
<!--Twitter Card-->
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@{{site.twitter}}">
<meta name="twitter:creator" content="@{{site.twitter}}">
<meta name="twitter:url" content="{{site.baseURL}}{{page.url}}">
<meta name="twitter:title" content="{{title}}">
<meta name="twitter:description" content="{{content | striptags | truncate(196) }}">
<meta name="twitter:image" content="{{featuredImageThumbnail}}">

<!--Schema-->
<link rel="author" href="{{site.baseURL}}">
<link rel="publisher" href="{{site.baseURL}}">
<meta itemprop="name" content="{{title}}">
<meta itemprop="description" content="{{content | striptags | truncate(196) }}">
<meta itemprop="image" content="{{featuredImageThumbnail}}">

<!-- Facebook OpenGraph -->
<meta property="fb:app_id" content="{{site.FB_APP_ID}}">
<meta property="og:url" content="{{site.baseURL}}{{page.url}}">
<meta property="og:type" content="website">
<meta property="og:title" content="{{title}}">
<meta property="og:image" content="{{featuredImageThumbnail}}">
<meta property="og:description" content="{{content | striptags | truncate(196) }}">
<meta property="og:site_name" content="{{site.title}}">
<meta property="og:locale" content="en_GB">
<meta property="article:author" content="{{site.baseURL}}">
{%- endblock -%}

Obviously wherever you see a {{site.XXXX}} variable it indicates site level data which is typically defined within a site.json file in your _data folder.

Ryuno-Ki commented 5 years ago

Wait, I can use blocks like in Jinja2/Django? :O

(You should have an option to declare noindex, e.g. for search result page)

zachleat commented 5 years ago

see also https://github.com/jekyll/jekyll-seo-tag

zachleat commented 5 years ago

This repository is now using lodash style issue management for enhancements. This means enhancement issues will now be closed instead of leaving them open.

View the enhancement backlog here. Don’t forget to upvote the top comment with 👍!