「ブログカード」というものを利用したい!

いろんなSNSでさ、リンクを貼ると、その記事のタイトルとか概要とかサムネイルが表示される奴あるじゃん?

あれが欲しい

あれをこのブログ内でリンクを張るときにも使いたい。

「ブログカード」と呼ばれているらしい

ふへーはてなブログで使われているものが有名?らしい

Hugoでやっている人がいた!!

AIちゃんにやり方を聞いたところ参照元として持ってきてくれた!!自分がやりたいことをやっている!!!

画像と、タイトルと概要的なものを表示できたらよいかなぁ~という気持ち

やるぜ!!!

ショートコード

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

{{- $title := "" -}}
{{- $description := "" -}}
{{- $image := "" -}}

{{- with try (resources.GetRemote $url) -}}
    {{- with .Err -}}
        {{- $title = (print "情報の取得に失敗しました( ;∀;)") -}}
    {{- else with .Value -}}
        {{- $content := .Content -}}
        {{- $head := index (findRE `(?s)<head[^>]*>.*?</head>` .Content ) 0 -}}

        {{/* タイトルの抽出(より柔軟なパターン) */}}
        {{- $titleMatch := findRE `<title[^>]*>(.*?)</title>` $content 1 -}}
        {{- if $titleMatch -}}
            {{- $title = replaceRE `<title[^>]*>(.*?)</title>` "$1" (index $titleMatch 0) -}}
        {{- end -}}

        {{/* メタタグの抽出 */}}
        {{- range $meta := findRE `<meta.*?>` $head -}}
            {{- $name := findRE `name=["']([^"']+)["']` $meta 1 -}}
            {{- if $name -}}
              {{- $name = replaceRE `name=["']([^"']+)["']` `$1` (index $name 0) -}}
            {{- end -}}

            {{- $property := findRE `property=["']([^"']+)["']` $meta 1 -}}
            {{- if $property -}}
              {{- $property = replaceRE `property=["']([^"']+)["']` `$1` (index $property 0) -}}
            {{- end -}}

            {{- $content := findRE `content=["']([^"']+)["']` $meta 1 -}}
            {{- if $content -}}
              {{- $content = replaceRE `content=["']([^"']+)["']` `$1` (index $content 0) -}}
            {{- end -}}

            {{- if eq $property "og:title" -}}
                {{- $title = $content -}}
            {{- else if eq $property "og:description" -}}
                {{- $description = $content -}}
            {{- else if eq $property "og:image" -}}
                {{- $image = $content -}}
            {{- end -}}
        {{- end -}}
    {{- end -}}
{{- end -}}

<a href="{{ $url }}" class="link-card-wrapper">
    <div class="link-card-inner">
      {{ if $image }}
        {{ $imgResource := resources.GetRemote $image | images.Filter images.AutoOrient }}
        {{ $resized := $imgResource.Fill "160x120 Center" }}
        {{ with $resized }}
          <div class="link-card-image">
            <img src="{{ .RelPermalink }}" alt="{{ $title }}">
          </div>
        {{ end }}
      {{ else }}
        <!-- 画像がない場合でもスペースを確保(任意) -->
        <!-- <div class="link-card-image" style="background-color:#f5f5f5;"></div> -->
      {{ end }}
  
      <div class="link-card-content">
        {{ if $title }}
          {{ $max := 40 }}
          {{ $shortTitle := cond (gt (len $title) $max) (printf "%s…" ($title | truncate  $max)) $title }}
          <div class="link-card-title" title="{{ $title }}">{{ $shortTitle | plainify }}</div>
        {{ end }}
        {{ if $description }}
          {{ $max := 80 }}
          {{ $shortDesc := cond (gt (len $description) $max) (printf "%s…" ($description | truncate $max)) $description }}
          <div class="link-card-description">{{ $shortDesc | plainify }}</div>
        {{ end }}
        <div class="link-card-url">{{ $url }}</div>
      </div>
    </div>
</a>

css

// ========== Link Card SCSS ========== //

$link-card-border-color: #ddd;
$link-card-border-color-dark: $global-border-color-dark;
$link-card-bg-color: #fff;
$link-card-bg-color-dark: #2b2b2b;
$link-card-text-color: inherit;
$link-card-text-color-dark: $global-font-color-dark;

$link-card-description-color: #555;
$link-card-description-color-dark: #ccc;

$link-card-url-color: #999;
$link-card-url-color-dark: #888;

.link-card-wrapper {
  display: block;
  max-width: 720px;
  margin-bottom: 1rem;
  border: 1px solid $link-card-border-color;
  border-radius: 8px;
  overflow: hidden;
  text-decoration: none;
  color: $link-card-text-color;
  background-color: $link-card-bg-color;
  transition: box-shadow 0.2s;

  &:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  }

  body[theme="dark"] & {
    border-color: $link-card-border-color-dark;
    background-color: $link-card-bg-color-dark;
    color: $link-card-text-color-dark;

    &:hover {
        box-shadow: 0 4px 12px rgba(255, 255, 255, 0.1);
    }
  }
}

.link-card-inner {
  display: flex;
  flex-direction: row;
  align-items: stretch;
}

.link-card-image {
  flex: 0 0 160px;
  height: 120px;
  overflow: hidden;

  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
}

.link-card-content {
  flex: 1;
  padding: 12px;
  display: flex;
  flex-direction: column;
  justify-content: center;

  .link-card-title {
    font-size: 1rem;
    font-weight: bold;
  }

  .link-card-description {
    font-size: 0.8rem;
    color: $link-card-description-color;

    body[theme="dark"] & {
      color: $link-card-description-color-dark;
    }
  }

  .link-card-url {
    font-size: 0.8rem;
    color: $link-card-url-color;

    body[theme="dark"] & {
      color: $link-card-url-color-dark;
    }
  }
}

@media (max-width: 768px) {

  .link-card-image {
    flex: 0 0 120px;
    height: 90px;
  }

  .link-card-content {
    padding: 8px;

    .link-card-title {
      font-size: 1rem;
    }

    .link-card-description {
      display: none;
    }

    .link-card-url {
      display: none;
    }
  }
}

できたと思いたい

できたものがこちら↓(そのままキャプチャー撮って貼ったので同化してわかりにくい) /posts/2025/05/creating_blog_card/image.png

↑ここまで

先人の知恵を借りているのはもちろんのことですが・・・

私はあほあほなのでほぼすべてAIちゃんにやってもらいました。

基本的には

resources.GetRemoteでとってきた情報から、findREreplaceREを利用して正規表現で必要な情報を取得して、それをもとにカードを作っている感じですね。

正規表現がマジで何もわからない!!!

なぜか自分が見た感じmetaの書き方は同じなのに、あるサイトではdescriptionが取れて、別のサイトでは取れないみたいな感じになっていて・・・

「なんでなの~~;;助けて~~」と泣きついて出されたコードを使うとなんか知らんが動くみたいな・・・

難しいですね・・・


画像に関してはfillを利用してサイズを調整!(しているつもり…これもよくわからん…)

なぜか、画像が上下反転された状態で表示されることがあったのですが、AutoOrientを利用すると元に戻りました。

scssに関してはAIちゃんがすべて作ってくれました!ダークモード対応もお願いしちゃいました!ありがたいね!!

疲れた~~~!!!

何とかそれっぽいものができてよかった・・・けどほぼ丸一日かかったんだけど!!!!これだけのために!!!!

正規表現とかさ・・・何かの使い方とかよくわからなかったりさ・・・

なんか、細かいところが気になったり、本当に関係がない部分(ブログテーマとして機能だけど、コード上のタイトル表示が言語名指定できないなぁ・・・とか自動で開いた状態にできないのかなぁ・・・みたいな)が気になっちゃったり・・・してさ!!!めちゃくちゃ時間かかったよ!!!

今度別の機会で、resources.GetRemoteみたいな?ものを利用して取ってきた情報をもとにSNSの投稿埋め込み~みたいなものやってみたいかも

それでは・・・!本当に疲れた!!!