From 53e68c3449e47da21c192ce3df746ee0eb717301 Mon Sep 17 00:00:00 2001 From: seieric <30688855+seieric@users.noreply.github.com> Date: Thu, 30 Jul 2026 16:57:31 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20OGP=E7=94=BB=E5=83=8F=E3=82=92front?= =?UTF-8?q?matter=E3=81=A7=E6=8C=87=E5=AE=9A=E5=8F=AF=E8=83=BD=E3=81=AB?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layouts/Layout.astro | 79 ++++++----------------------------- src/layouts/SEO.astro | 89 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 66 deletions(-) create mode 100644 src/layouts/SEO.astro diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 05ccf0cdf1..a06f8dd64a 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -1,18 +1,17 @@ --- -import { SEO, type Props as SEOProps } from "astro-seo"; +import SEO, { type OgpImage } from "./SEO.astro"; import GoogleAnalytics from "./GoogleAnalytics.astro"; import MicrosoftClarity from "./MicrosoftClarity.astro"; import Header from "./Header/index.astro"; import Footer from "./Footer/index.astro"; import Toc from "./Toc.astro"; import "@styles/layout.scss"; -import type { MDXLayoutProps, MarkdownHeading } from "astro"; +import type { MDXLayoutProps } from "astro"; import Author, { type Author as AuthorProps } from "./Author.astro"; import Breadcrumb from "./Breadcrumb.astro"; import type { Breadcrumb as BreadcrumbProps } from "./resolve-breadcrumb"; import Markdown from "@components/utils/Markdown.astro"; import type { Lang } from "@components/types"; -import { parse as parsePath } from "path"; import "@fontsource/material-icons"; import "@fontsource-variable/open-sans"; import resolveBreadcrumb from "./resolve-breadcrumb"; @@ -33,36 +32,20 @@ export interface Frontmatter { redirect_to?: string; author?: AuthorProps; breadcrumb?: BreadcrumbProps; + ogpImage?: OgpImage; } -function createDefaultDescription( - lang: Lang, - headings: MarkdownHeading[], -): string { - return ( - ( - { - ja: "目次: ", - en: "Table of Contents: ", - }[lang] + - headings - .filter(({ depth }) => depth === 2) - .map(({ text }) => text) - .join("; ") - ).slice(0, 97) + "..." - ); -} - -const { frontmatter, headings, file } = Astro.props; +const { frontmatter, headings } = Astro.props; const { title, - description = createDefaultDescription(frontmatter.lang, headings), + description, lang, h1, top, support, author, breadcrumb, + ogpImage, } = frontmatter; const displayToc = !top && (frontmatter.toc ?? true) && headings.length > 0; @@ -77,53 +60,11 @@ const htmlAttributes = { }, }[lang]; -const locale = { - en: "en_US", - ja: "ja_JP", -}[lang]; - const gaid = "G-ZM6H96EWBD"; const clarityProjectId = "vat2444uow"; const canonicalUrl = Astro.url; -const seoProps: SEOProps = { - title, - titleTemplate: "%s | utelecon", - titleDefault: "utelecon", - description, - charset: "UTF-8", - canonical: canonicalUrl.href, -}; - -const ogpImagePath = { - en: "/assets/images/ogp_en.png", - ja: "/assets/images/ogp.png", -}[lang]; - -if (title) { - seoProps.openGraph = { - basic: { - title, - type: "website", - url: canonicalUrl.href, - image: new URL(ogpImagePath, Astro.site).href, - }, - optional: { - description, - locale, - siteName: "utelecon", - }, - image: { - alt: "utelecon", - }, - }; - seoProps.twitter = { - title, - description, - }; -} - const resolvedBreadcrumb = breadcrumb && (await resolveBreadcrumb(Astro.url, breadcrumb)); --- @@ -131,7 +72,13 @@ const resolvedBreadcrumb = - + diff --git a/src/layouts/SEO.astro b/src/layouts/SEO.astro new file mode 100644 index 0000000000..80fe5af491 --- /dev/null +++ b/src/layouts/SEO.astro @@ -0,0 +1,89 @@ +--- +import { SEO as AstroSEO, type Props as SEOProps } from "astro-seo"; +import type { MarkdownHeading } from "astro"; +import type { Lang } from "@components/types"; + +interface Props { + title?: string; + description?: string; + lang: Lang; + headings?: MarkdownHeading[]; + ogpImage?: OgpImage; +} + +export interface OgpImage { + ja?: string; + en?: string; +} + +const { title, description, lang, headings = [], ogpImage } = Astro.props; + +function createDefaultDescription( + lang: Lang, + headings: MarkdownHeading[], +): string { + return ( + ( + { + ja: "目次: ", + en: "Table of Contents: ", + }[lang] + + headings + .filter(({ depth }) => depth === 2) + .map(({ text }) => text) + .join("; ") + ).slice(0, 97) + "..." + ); +} + +const resolvedDescription = + description ?? createDefaultDescription(lang, headings); + +const locale = { + en: "en_US", + ja: "ja_JP", +}[lang]; + +const canonicalUrl = Astro.url; + +const seoProps: SEOProps = { + title, + titleTemplate: "%s | utelecon", + titleDefault: "utelecon", + description, + charset: "UTF-8", + canonical: canonicalUrl.href, +}; + +const defaultOgpImagePath = { + en: "/assets/images/ogp_en.png", + ja: "/assets/images/ogp.png", +}[lang]; + +const ogpImagePath = ogpImage?.[lang] ?? defaultOgpImagePath; + +if (title) { + seoProps.openGraph = { + basic: { + title, + type: "website", + url: canonicalUrl.href, + image: new URL(ogpImagePath, Astro.site).href, + }, + optional: { + description: resolvedDescription, + locale, + siteName: "utelecon", + }, + image: { + alt: "utelecon", + }, + }; + seoProps.twitter = { + title, + description: resolvedDescription, + }; +} +--- + + From 751bfc1ed8b2d93d944033d01e9e2aeb2e752131 Mon Sep 17 00:00:00 2001 From: seieric <30688855+seieric@users.noreply.github.com> Date: Thu, 30 Jul 2026 17:06:55 +0900 Subject: [PATCH 2/4] =?UTF-8?q?add:=20README=E3=81=ABOGP=E7=94=BB=E5=83=8F?= =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E3=81=AE=E8=AA=AC=E6=98=8E=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 18757bff74..64523af4cd 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Markdownファイルのフロントマターにかける設定は以下の通り - `name`:著者名.任意. - `breadcrumb`:ページ上部のパンくずリストを定義する.詳しくは[後述](#パンくずリストの表示). - `redirect_from`・`redirect_to`:リダイレクトの設定.詳しくは[後述](#redirect_from--redirect_to). +- `ogpImage`: ページに任意のOGP画像を設定する. 詳しくは[後述](#任意のOGP画像を設定する) ## Scripts @@ -271,6 +272,25 @@ pattern: "^\/utol\/" --- ``` +### 任意のOGP画像を設定する + +`ogpImage`をMarkdownのfrontmatterに記述することで,SNS共有時などに表示されるOGP画像をページごとに個別設定できます. +言語ごとに任意のOGP画像を設定可能となっており,設定がない場合はデフォルトのOGP画像が利用されます. + +指定する画像は`public/assets/images/`以下に配置し,`/assets/images/custom_ogp_ja.png`のように指定してください. + +### 記述例 + +```md +--- +title: "記事のタイトル" +lang: "ja" +ogpImage: + ja: "/assets/images/custom_ogp_ja.png" + en: "/assets/images/custom_ogp_en.png" +--- +``` + ### ボタン・タブUIを利用したい場合 [多要素認証の初期設定手順](https://utelecon.adm.u-tokyo.ac.jp/utokyo_account/mfa/initial/)や[サポート窓口](https://utelecon.adm.u-tokyo.ac.jp/support/)のページでは,ボタンとタブを用いたUIが利用されており,ユーザの選択によって表示内容を切り替えたり,ボタンをクリックして別のページに遷移したりすることができます. From 5eb7a662bb5b72c0d159c828807b63721c40a4b2 Mon Sep 17 00:00:00 2001 From: seieric <30688855+seieric@users.noreply.github.com> Date: Fri, 31 Jul 2026 18:11:36 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix=20=E4=B8=8D=E8=A6=81=E3=81=AA=E8=A8=80?= =?UTF-8?q?=E8=AA=9E=E5=88=87=E6=9B=BF=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layouts/Layout.astro | 4 ++-- src/layouts/SEO.astro | 9 ++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index a06f8dd64a..b8353c111a 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -1,5 +1,5 @@ --- -import SEO, { type OgpImage } from "./SEO.astro"; +import SEO from "./SEO.astro"; import GoogleAnalytics from "./GoogleAnalytics.astro"; import MicrosoftClarity from "./MicrosoftClarity.astro"; import Header from "./Header/index.astro"; @@ -32,7 +32,7 @@ export interface Frontmatter { redirect_to?: string; author?: AuthorProps; breadcrumb?: BreadcrumbProps; - ogpImage?: OgpImage; + ogpImage?: string; } const { frontmatter, headings } = Astro.props; diff --git a/src/layouts/SEO.astro b/src/layouts/SEO.astro index 80fe5af491..9163b360a5 100644 --- a/src/layouts/SEO.astro +++ b/src/layouts/SEO.astro @@ -8,12 +8,7 @@ interface Props { description?: string; lang: Lang; headings?: MarkdownHeading[]; - ogpImage?: OgpImage; -} - -export interface OgpImage { - ja?: string; - en?: string; + ogpImage?: string; } const { title, description, lang, headings = [], ogpImage } = Astro.props; @@ -60,7 +55,7 @@ const defaultOgpImagePath = { ja: "/assets/images/ogp.png", }[lang]; -const ogpImagePath = ogpImage?.[lang] ?? defaultOgpImagePath; +const ogpImagePath = ogpImage ?? defaultOgpImagePath; if (title) { seoProps.openGraph = { From b2cb559923a72b2d466879723d77d4f25f86bd3e Mon Sep 17 00:00:00 2001 From: seieric <30688855+seieric@users.noreply.github.com> Date: Fri, 31 Jul 2026 18:12:32 +0900 Subject: [PATCH 4/4] update README --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 64523af4cd..dc62a7f06d 100644 --- a/README.md +++ b/README.md @@ -274,10 +274,9 @@ pattern: "^\/utol\/" ### 任意のOGP画像を設定する -`ogpImage`をMarkdownのfrontmatterに記述することで,SNS共有時などに表示されるOGP画像をページごとに個別設定できます. -言語ごとに任意のOGP画像を設定可能となっており,設定がない場合はデフォルトのOGP画像が利用されます. +`ogpImage`をMarkdownのfrontmatterに記述することで,SNS共有時などに表示されるOGP画像をページごとに個別設定できます.設定がない場合はデフォルトのOGP画像が利用されます. -指定する画像は`public/assets/images/`以下に配置し,`/assets/images/custom_ogp_ja.png`のように指定してください. +指定する画像は`public/assets/images/`以下に配置し,`/assets/images/custom_ogp.png`のように指定してください. ### 記述例 @@ -285,9 +284,7 @@ pattern: "^\/utol\/" --- title: "記事のタイトル" lang: "ja" -ogpImage: - ja: "/assets/images/custom_ogp_ja.png" - en: "/assets/images/custom_ogp_en.png" +ogpImage: "/assets/images/custom_ogp.png" --- ```