Hugoでシンプルなリンクカード(ブログカード)を作った

Hugoでシンプルなリンクカード(ブログカード)を作りました。リンクカードというのは以下のようにリンク先の情報をまとめて表示したものです(崩れていたら教えてください)。

今回はHugoの機能のみを使い、外部ライブラリなどにも依存しないコードとして実装しています。

参考サイト

以下のサイト様を参考にしました。

しかし手元ではうまく動かなかったり、使っているCSSが違ったりしたので、大枠は参考にさせてもらいつつなるべく環境によらず動くことやコード自体複雑になりすぎないことを意識して自分で実装してみました。

方針

基本は上記サイトと同じく、GetRemoteという関数で外部サイトの情報を取得し、そこから表示したい内容を抽出します。

今回はシンプルにタイトルとfaviconのみを使います。

タイトル

タイトルは og:title ではなくhead内のtitle要素を取ってきています。まず og:title の実装状況がわからないですが head 内の title はほぼ間違いなく入っているだろうと思われます。

また headtitle にはサイト名も入っている場合が多く、 og:titleog:site_name を組み合わせて出すよりも title をそのまま出す方が便利だと思ったからです(中には og:title にサイト名が入っているものもありました)。

アイコン

アイコンはサイト側の実装に幅があるのですが、以下の順に見て最初にヒットしたものを表示しています。サイズ違いなどいろいろあるので本当はもっと凝りたいところですが、一旦ここでは細かく見ずに見つかったものを表示しています。

  1. head内の <link rel="icon" href="...">
  2. head内の <link rel="shurtcut icon" href="...">
  3. ルート直下のfaviconファイル
    1. /favicon.ico
    2. /favicon.png
    3. /favicon.gif

サムネイル画像

サムネイル画像を表示するサービスもよくあります。ただし画像は用意されてない場合の対応やどこをどう切り取るかなど、考えることが多いのが難点です。

また画像はサイトの雰囲気にも強く影響を与えるため、あえて今回の実装からは抜いておこうと考えました。

実際のコード

上記の処理を行うのが以下のコードです。これをHugo内でshortcodeとして保存します。以下では linkcard.html としています。

{{- $url := (.Get 0) -}}
{{- $target_url := urls.Parse $url -}}

{{- $title := "" -}}
{{- $favicon_url := "" -}}

{{- with $result := resources.GetRemote $url -}}
    {{- with $result.Err -}}
      {{- $title = (print $url "にアクセスできませんでした") -}}
    {{- else -}}
        <!-- headを取得 -->
        {{- $head := index (findRE "<head[^>]*?>(.|\n)*?</head>" $result.Content) 0 -}}

        <!-- headからタイトルを取得 -->
        {{- $title = index (findRE "<title.*?>(.|\n)*?</title>" $head) 0 -}}
        {{- if $title -}}
          {{- $title = replaceRE "</?title>" "" $title -}}
        {{- end -}}

        <!-- headからfaviconを取得 -->
        {{- $linktag := index (findRE "<link[^<>]*rel=[\"']?icon[\"']?.*?>" $head) 0 -}}
        {{- if not $linktag -}}
          {{- $linktag = index (findRE "<link[^<>]*?rel=[\"']?shortcut icon[\"']?.*?>" $head) 0 -}}
        {{- end -}}
        {{- if $linktag -}}
          {{- $href := index (findRE "href=[\"']?[^ >]*[\"']?" $linktag) 0 -}}
          {{- $href = replace $href "href=" "" | replaceRE "[\"']" "" -}}
          {{- if eq (strings.Substr $href 0 1) "/"  -}}
            {{- $favicon_url = print $target_url.Scheme "://" $target_url.Hostname $href -}}
          {{- else if gt (len (findRE "^http[s]?://" $href)) 0 -}}
            {{- $favicon_url = $href -}}
          {{- end -}}
        {{- end -}}

        <!-- /favicon.icoを取得 -->
        {{- if not $favicon_url -}}
          {{- range $favicon_ext := (slice ".ico" ".png" ".gif") -}}
            {{- $root_favicon_path := print $target_url.Scheme "://" $target_url.Hostname "/favicon" $favicon_ext -}}
            {{- $favicon := resources.GetRemote $root_favicon_path -}}
           {{- if not $favicon -}}
             {{- continue -}}
           {{- else -}}
             {{- $favicon_url = $root_favicon_path -}}
             {{- break -}}
            {{- end -}}
          {{- end -}}
        {{- end -}}
    {{- end -}}
{{- end -}}

<!-- HTMLを生成 -->
<a href="{{- htmlUnescape $target_url -}}">
  <div class="link-card">
    <div class="link-card-title">
      {{- $title | htmlUnescape | truncate 100 -}}
    </div>
    <div class="link-card-hostname">
      {{ if $favicon_url }}
      <div class="link-card-hostname-img">
        <img alt="icon" src="{{- $favicon_url -}}">
      </div>
      {{ end }}
      <span>
        {{- $target_url.Hostname -}}
      </span>
    </div>
  </div>
</a>

次に以下のCSSをスタイルとして使います。

.link-card {
  display: flex;
  flex-direction: column;
  padding: 1.5em;
  margin-top: 1em;
  margin-bottom: 1em;
  /* 以下はお好みで */
  border:solid 2px rgba(0,0,0,0.15);
  border-radius:5px;
}

/* マウスホバー時の挙動 */
.link-card:hover {
  opacity: 0.6;
  transition: 0.1s;
}

.link-card-title {
  font-weight: bold;
  margin-bottom: 0.8em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.link-card-hostname {
  display: flex;
  align-items: center;
  height: 1.5em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.link-card-hostname-img {
  height: 100%;
  display: flex;
  align-items: center;
}

.link-card-hostname-img img {
  max-height: 100%;
  width: auto;
  margin-right: 1em;
}

.link-card-hostname span {
  font-size: 0.9em;
}

記事の中で以下のようにするとリンクカードが表示されます。

{{< linkcard "https://example.com/" >}}

基本的に上記のコードは外部への依存が無いはずなのでそのままで動くかと思います。スタイルはお使いのテーマに合わせて細かく調整してみてください。

まとめ

Hugoでシンプルなリンクカード(ブログカード)を外部に依存無く実現するshortcodeを実装しました。何か不備などがあれば教えていただけると幸いです。

最終更新 2024-01-08

広告

本記事はお役に立てたでしょうか。本ブログでは匿名でのコメントや少額から(15円~)の寄付などを受け付けております。もしお役に立てたのであればご支援いただけると大変励みになります。

Built with Hugo
テーマ StackJimmy によって設計されています。