Page MenuHomePhabricator

Implement OpenGraph metadata in Phame for Twitter cards
Open, NormalPublic


See T13018 for previous discussion. I'm separating this out since "Twitter OpenGraph" and "Discourse OpenGraph/OneBox" are related but not really the same, and I think the "Phame + Twitter" use case is generally more compelling than the "Maniphest + Discourse" use case.

The Twitter card guidance is here:

The properties we can specify are:

  • twitter:card as "summary", "summary_large_image", "app", or "player". Unclear what the difference is, but probably "summary" or "summary_large_image" are what we want. It's possible this should be configurable but we can likely guess.
  • twitter:site is a Twitter @handle for the originating website, like @AcmeBlog. We could let users specify this on posts and/or provide a default on blogs.
  • twitter:creator is a Twitter @handle for the author. We could let users specify this on posts, and/or provide a default on blogs. If users have a connected Twitter account for auth we could use that too, but I don't like making authentication details like this public by default. I'm somewhat inclined to just skip this for now.
  • og:url should be the canonical URI.
  • og:title should probably be the post title. Allowing users to customize this feels like it's probably overboard on settings.
  • og:summary we can default to summarizing the content, but I think it's reasonable to also offer a "Summary" on posts that can override this. We can use this on the main Phame article list, too, if authors want to choose a summary which is different from the first few sentences of the post.
  • og:image we can use the post header image and fall back to the blog header image for now.

So the storage changes to blogs are:

  • Method to specify the default content @handle.
  • Method to specify the default author @handle.

The storage changes to posts are:

  • Method to override the post summary. This can be reused on the blog list.
  • Method to specify the content @handle.
  • Method to specify the author @handle.

The stuff in italics is probably not worth implementing for now.

I'd like to hide the OG detail stuff away from the main edit flow, but this is straightfoward.

(I'd also like to at least partially realign this on the new "document" layout that comes out of T13410, e.g. the "Actions" moving out of "Actions" and into a right-hand sidebar in editable views. Blog posts probably shouldn't have persistent navigation, but we could think about this or make it an option.)

Event Timeline

epriestley triaged this task as Normal priority.Sep 19 2019, 9:27 PM
epriestley created this task.

og:title should probably be the post title.

Another possible candidate is something like "Blog Name • Post Title", depending on what the cards actually look like.

I did some trial and error last week with Twitter via That was for my personal blog, but some of it might apply here as well.

Twitter Card (as of 9 Oct 2019).


  • og:title: Displayed. Trimmed at 55 characters for summary_large_image cards (image on top, title below it with full width). Trimmed at 40 characters for summary cards (reserves some space aside the title for an image),
  • og:description: Displayed.
  • og:site_name: Ignored. (URL domain is displayed instead.)
  • og:author: Ignored. (URL domain is displayed instead.)
  • og:image: Displayed, but only if og:image:alt is set as well (wow!). Twitter will generate a small thumbnail and serve it from their CDN, however the original must still be no larger than 4096x4096 pixels or 5MB in size.
  • og:image:alt: Used as image alt, naturally (must be under 420 characters).
opengraph.html (Jekyll include)
<meta property="og:title" content="{{ post.title | default: site.title | strip_html | normalize_whitespace | escape }}">

{% assign excerpt = post.content | first_paragraph %}
<meta property="og:description" content="{{ excerpt | strip_html | normalize_whitespace | truncate: 210 | escape }}">

{% if post.og_image_url and post.og_image_alt %}
{% if post.og_image_prominence != "aside" %}
<meta property="twitter:card" content="summary_large_image">
{% endif %}
<meta property="og:image" content="{{ post.og_image_url | absolute_url | escape }}">
<meta property="og:image:alt" content="{{ post.og_image_alt | escape }}">
{% endif %}

<meta property="og:type" content="article">
<meta property="article:published_time" content="{{ | date_to_xmlschema | escape }}">