Ghost: Metatags pflegen und automatisch generieren lassen

Wer einen Ghost-Blog hat, möchte ihn vielleicht auch etwas für Suchmaschinen oder die Verlinkung innerhalb von Social Media-Plattformen wie Twitter oder Facebook optimieren. Zumindest ging es mir so, weswegen ich es mir zur Aufgabe gemacht habe, meine Metatags auf allen generierten Ghost-Seiten zu überarbeiten.

Auf vielen Seiten werden schon sehr viele sinnvolle Meta-Informationen ausgegeben; am meisten natürlich auf einer Post-Seite. Damit aber auch die anderen Seiten wie Autoren-, Tag- und Startseite mit eben solchen Informationen angereichert werden, habe ich mir eine eigene kleine meta.hbs zusammengestrickt.

In diesem Blogpost möchte ich euch nun eine kleine Einführung in die Welt der .hbs-Dateien geben (so wie ich das verstanden habe, denn Profi bin ich da noch lange nicht!) und euch zeigen, wie ihr sie nutzen könnt, um je nach Seite andere Inhalte ausgeben zu können.

Was ist eine .hbs-Datei?

.hbs steht für handlebars; das ist wiederum der Name einer Templating Engine: Handlebars.js

Ghost benutzt diese Templating Engine, um die Logik von der HTML-Generierung (dem Template) zu trennen. So ist es relativ leicht möglich, innerhalb des HTMLs Platzhalter zu platzieren, welche dann von der Logik mit Ausgaben befüllt werden.

Mehr Informationen zum Thema Handlebars innerhalb von Ghost »

Welche Metatags brauche ich überhaupt?

Das ist eine Frage, über die man Stunden und Tage philosophieren könnte. Ich habe mich hier für die Integration der allgemein üblichen Metatags entschieden. Ihr könnt natürlich alle möglichen Metatags integrieren. Achtet nur darauf, dass ihr keine doppelten integriert. Wie weiter oben schon erwähnt, setzt Ghost selbstständig zum Beispiel schon einen Großteil aller Open Graph-Metatags.

Außerdem habe ich mich als aktiver Twitternutzer für die Integration der Twitter Cards entschieden. Wenn ihr das auch möchtet, müsst ihr eure Domain erst von Twitter für die Nutzung der Cards freischalten lassen. Das funktioniert innerhalb des Card Validators. Wenn ihr eure implementierten Tags dort zum ersten Mal testet, wird unterhalb des Inputs ein Hinweis erscheinen, dass eure Domain nicht ge-whitelist-ed ist. Klickt ihr auf den Link, müsst ihr nur ein Formular ausfüllen und das sollte es dann sein.

meta.hbs

Wir wollen uns nun eine Handlebars-Datei erstellen, welche sich um die Ausgabe der Metatags kümmert. Als Vorlage habe ich dazu auf eine .hbs-Datei von Oleg Fomin zurückgegriffen.

Wenn wir diese Datei als Ausgangsbasis für unsere eigene Informationsanreicherung benutzen, und dann auch noch Rücksicht darauf nehmen, was Ghost selbst schon im {{ghost_head}} generiert, kommen wir auf folgenden Dateiinhalt:

{{#is "index, tag, author"}} {{!-- Meta for index, tag and author contexts --}}
{{#is "index"}}
<title>{{@blog.title}} &middot; blog.katharinafranz.com</title>  
<meta property="og:title" content="{{@blog.title}} &middot; blog.katharinafranz.com"/>  
<meta name="description" content="{{@blog.description}}"/>  
<meta itemprop="name" content="{{@blog.title}}"/>  
<meta name="twitter:card" content="summary"/>  
<meta name="twitter:title" content="{{@blog.title}} &middot; blog.katharinafranz.com"/>  
<meta name="twitter:description" content="{{@blog.description}}"/>  
<meta name="twitter:image" content="{{@blog.url}}{{@blog.cover}}"/>  
<meta name="twitter:site" content="@heartcrazed"/>  
<meta name="twitter:creator" content="@heartcrazed"/>  
{{/is}}
{{#is "tag"}}
<title>Artikel zum Thema »{{tag.name}}« &middot; blog.katharinafranz.com</title>  
<meta name="description" content="{{@blog.description}}"/>  
<meta property="og:title" content="Artikel zum Thema »{{tag.name}}« &middot; blog.katharinafranz.com"/>  
<meta itemprop="name" content="Artikel zum Thema »{{tag.name}}« &middot; blog.katharinafranz.com"/>  
<meta name="twitter:card" content="summary"/>  
<meta name="twitter:title" content="Artikel zum Thema »{{tag.name}}« &middot; blog.katharinafranz.com"/>  
<meta name="twitter:description" content="{{@blog.description}}"/>  
<meta name="twitter:image" content="{{@blog.url}}{{@blog.cover}}"/>  
<meta name="twitter:site" content="@heartcrazed"/>  
<meta name="twitter:creator" content="@heartcrazed"/>  
{{/is}}
{{#is "author"}}
<title>Artikel von {{author.name}} &middot; blog.katharinafranz.com</title>  
<meta name="description" content="{{@blog.description}}"/>  
<meta property="og:title" content="Artikel von {{author.name}} &middot; blog.katharinafranz.com"/>  
<meta itemprop="name" content="Artikel von {{author.name}} &middot; blog.katharinafranz.com"/>  
<meta name="twitter:card" content="summary"/>  
<meta name="twitter:title" content="Artikel von {{author.name}} &middot; blog.katharinafranz.com"/>  
<meta name="twitter:description" content="{{@blog.description}}"/>  
<meta name="twitter:image" content="{{@blog.url}}{{@blog.cover}}"/>  
<meta name="twitter:site" content="@heartcrazed"/>  
<meta name="twitter:creator" content="@heartcrazed"/>  
{{/is}}
<meta property="og:type" content="website"/>  
<meta property="og:site_name" content="{{@blog.title}} &middot; blog.katharinafranz.com"/>  
<meta property="og:url" content="{{@blog.url}}{{{relativeUrl}}}"/>  
<meta property="og:description" content="{{@blog.description}}"/>  
<meta itemprop="description" content="{{@blog.description}}"/>  
{{#if @blog.cover}}
<meta property="og:image" content="{{@blog.url}}{{@blog.cover}}"/>  
<link rel="image_src" href="{{@blog.url}}{{@blog.cover}}"/>  
<meta itemprop="image" content="{{@blog.url}}{{@blog.cover}}"/>  
{{/if}}
{{/is}} {{!-- End of index, tag and author contexts --}}
{{#is "post, page"}} {{!--  Meta for post and page contexts --}}
<title>{{meta_title}} &middot; blog.katharinafranz.com</title>  
<meta itemprop="name" content="{{meta_title}} &middot; blog.katharinafranz.com"/>  
<meta name="twitter:site" content="@heartcrazed"/>  
<meta name="twitter:creator" content="@heartcrazed"/>  
{{#if post.meta_description }}
<meta name="description" content="{{meta_description}}"/>  
<meta itemprop="description" content="{{meta_description}}"/>  
{{else}}
{{#post}}
<meta name="description" content="{{excerpt words="27"}} &hellip;"/>  
<meta itemprop="description" content="{{excerpt words="27"}} &hellip;"/>  
{{/post}}
{{/if}}
{{#post}}
{{#if image}}
<meta property="og:image" content="{{@blog.url}}{{image}}"/>  
<link rel="image_src" href="{{@blog.url}}{{image}}"/>  
<meta itemprop="image" content="{{@blog.url}}{{image}}"/>  
{{else}}
{{#if @blog.cover}}
<meta property="og:image" content="{{@blog.url}}{{@blog.cover}}"/>  
<link rel="image_src" href="{{@blog.url}}{{@blog.cover}}"/>  
<meta itemprop="image" content="{{@blog.url}}{{@blog.cover}}"/>  
{{/if}}
{{/if}}
{{/post}}
{{/is}} {{!--  End of post and page contexts --}}
<!-- Ghost Head -->  
{{ghost_head}} {{!-- Note: many `og:properties` are generated by ghost_head in post and page contexts --}}

Achtung: Wenn ihr euch diesen Code für eure eigene Webseite schnappt, denkt bitte daran, einige Zeilen an eure Bedürfnisse anzupassen. So werdet ihr in diesen Codezeilen desöfteren meinen Blog- oder Twitternamen finden. Passt dies also bitte noch an.

Diese Zeilen speichert ihr als meta.hbs ab und legt die Datei in euer Theme-Verzeichnis in den Ordner partials. Um diese Meta-Angaben nun auch im Template auszugeben, bedarf es noch einer Zeile in der Datei default.hbs in eurem Template-Rootverzeichnis.

Fügt einfach an der Stelle, wo eure Metatags ausgegeben werden sollen, folgende Zeile ein:

{{> "meta"}}

Dieser Befehl sagt der Templating Engine, dass hier der Inhalt der meta.hbs hinein gerendert werden soll.

meta.hbs – Details

Damit ihr auch versteht, wie genau die meta.hbs funktioniert, werde ich euch hier die einzelnen Code-Blöcke erklären.

Eine Anmerkung vorab: Ich habe die Einrückung oben mit Absicht weggelassen, da die Einrückung leider auch in die letztendlich generierte HTML-Seite übernommen wird. So könnt ihr euch die Zeilen einfach kopieren und anpassen. Zur Erklärung hier werde ich die einzelnen Blöcke jedoch wieder einrücken. Denkt nur daran: wenn ihr sauber generiertern Code haben möchtet, entfernt alle Leerzeichen vor den Zeilen.

Meta-Angaben für die Index-, Autoren- und Tag-Seiten

Die ersten ca. 50 Zeilen der Datei behandeln den Fall, dass die Metatags auf den Seiten für die Autoren und Tags sowie die Startseite ausgegeben werden. Dafür gibt es die Abfrage, auf welchem Seitentyp man sich befindet ({{#is "index, tag, author"}} bzw. {{#is "SEITENTYP"}}). Ergibt diese Abfrage ein true, so werden die Tags innerhalb dieses Block ausgegeben. Beispielhaft zeige ich euch dies anhand der Startseite (index). Die Auslassungspunkte zeigen euch an, dass hier noch die Abfrage für die anderen zwei Seitentypen folgen würden. Der Part danach gilt für alle drei Seitentypen gemeinsam. Dort wird auch nochmal abgefragt, ob ein Cover-Image existiert. Dazu wird mit der gleichen is-Abfrage geprüft, ob @blog.cover gesetzt ist. Falls ja, werden auch die Metatags innerhalb dieses Blogs ausgegeben.

{{#is "index, tag, author"}}
    {{#is "index"}}
        <title>{{@blog.title}} &middot; blog.katharinafranz.com</title>
        <meta property="og:title" content="{{@blog.title}} &middot; blog.katharinafranz.com"/>
        <meta name="description" content="{{@blog.description}}"/>
        <meta itemprop="name" content="{{@blog.title}}"/>
        <meta name="twitter:title" content="{{@blog.title}} &middot; blog.katharinafranz.com"/>
    {{/is}}

    …

    <meta property="og:type" content="website"/>
    <meta property="og:site_name" content="{{@blog.title}} &middot; blog.katharinafranz.com"/>
    <meta property="og:url" content="{{@blog.url}}{{{relativeUrl}}}"/>
    <meta property="og:description" content="{{@blog.description}}"/>
    <meta itemprop="description" content="{{@blog.description}}"/>
    {{#if @blog.cover}}
        <meta property="og:image" content="{{@blog.url}}{{@blog.cover}}"/>
        <link rel="image_src" href="{{@blog.url}}{{@blog.cover}}"/>
        <meta itemprop="image" content="{{@blog.url}}{{@blog.cover}}"/>
    {{/if}}
    <meta name="twitter:card" content="summary"/>
    <meta name="twitter:description" content="{{@blog.description}}"/>
    <meta name="twitter:image" content="{{@blog.url}}{{@blog.cover}}"/>
    <meta name="twitter:site" content="@heartcrazed"/>
    <meta name="twitter:creator" content="@heartcrazed"/>
{{/is}}

Wie ihr seht, finden sich hier einige Platzhalter (genannt Helper, erkennbar an dem @) für Felder, die sich zum Beispiel in den Blogeinstellungen verbergen:

  • TITLE – {{@blog.title}}
  • DESCRIPTION – {{@blog.description}}
  • TYPE – website
  • URL – {{@blog.url}}{{{relativeUrl}}}
  • SITE_NAME – {{@blog.title}}
  • IMAGE – {{@blog.url}}{{@blog.cover}} (Falls es ein Blog-Cover gibt.)

Eine vollständige Liste aller Helper finden sich in der Ghost Documentation.

Meta-Angaben für Pages (Seiten) und Posts (Beiträge)

Hier der komplette Teil, für die bessere Lesbarkeit eingerückt, welcher sich um die Ausgabe der Metatags auf Pages und Posts dreht:

{{#is "post, page"}}
    <title>{{meta_title}} &middot; blog.katharinafranz.com</title>
    <meta itemprop="name" content="{{meta_title}} &middot; blog.katharinafranz.com"/>
    <meta name="twitter:site" content="@heartcrazed"/>
    <meta name="twitter:creator" content="@heartcrazed"/>
    {{#if post.meta_description }}
        <meta name="description" content="{{meta_description}}"/>
        <meta itemprop="description" content="{{meta_description}}"/>
    {{else}}
        {{#post}}
            <meta name="description" content="{{excerpt words="27"}} &hellip;"/>
            <meta itemprop="description" content="{{excerpt words="27"}} &hellip;"/>
        {{/post}}
    {{/if}}
    {{#post}}
        {{#if image}}
        <meta property="og:image" content="{{@blog.url}}{{image}}"/>
        <link rel="image_src" href="{{@blog.url}}{{image}}"/>
        <meta itemprop="image" content="{{@blog.url}}{{image}}"/>
        {{else}}
            {{#if @blog.cover}}
                <meta property="og:image" content="{{@blog.url}}{{@blog.cover}}"/>
                <link rel="image_src" href="{{@blog.url}}{{@blog.cover}}"/>
                <meta itemprop="image" content="{{@blog.url}}{{@blog.cover}}"/>
            {{/if}}
        {{/if}}
    {{/post}}
{{/is}}

Zum besseren Verständnis, auch hier die Erklärung:

Zuerst wird natürlich abgefragt, ob man sich auf einer Seite des Typs page oder post befindet. Ist dies der Fall, folgen zuerst die allgemeinen, also für beide Seitentypen identischen Tags. Danach folgt eine Zwischenabfrage, ob eine eigene Meta-Description innerhalb der Page/Posts gepflegt wurde. Wenn dem so ist, soll natürlich diese ausgegeben werden. Wenn nicht (und es handelt sich um einen Post), soll das Exzerpt des Beitrags (die ersten 27 Wörter) genutzt werden.

Desweiteren soll, wenn es sich um einen Post handelt, das Post-Image für alle Image-Metatags genutzt werden, falls eines gepflegt wurde. Wenn nicht, soll das Cover als Image genutzt werden. Wenn auch das nicht vorhanden ist, werden gar keine bildbezogenen Image-Metatags ausgegeben.

Die hier verwendeten Helper:

  • TITLE – {{meta_title}}
  • DESCRIPTION – {{meta_description}} bzw. {{excerpt words="27"}})
  • IMAGE – {{@blog.url}}{{image}} bzw. {{@blog.url}}{{@blog.cover}}

Wenn ihr mit euren Anpassungen zufrieden seid, müsst ihr den Ghost-Prozess auf eurem Server vermutlich neu starten. Ich hoffe, ich konnte euch einen kleinen Einblick in das Arbeiten mit Handlebars sowie ein paar Tipps für eure eigenen Metatags geben.

Es würde mich freuen, wenn ihr mir in den Kommentaren verratet, ob euch dieser Codeschnipsel etwas genutzt hat bzw. wenn ihr ihn für euren eigenen Ghost-Blog verwendet.

Katharina

Ich bin Jahrgang ‘89, »digital native« und arbeite als Web-Entwicklerin in Frankfurt. Ich mag Webdesign, Fotografie, mein iPhone, Cookies und Latte Macchiato.

Du möchtest keinen Beitrag mehr verpassen und alle direkt in dein Mail-Postfach geschickt bekommen? Kein Problem! (Es gibt keinen Spam, versprochen!)

Alternative: RSS-Feed via Feedly