すたすた式
Blogger web
ブログカード(カード型リンク)を作成するブックマークレットを作成しました。
プロンプトを表示するタイプと、インフォメーションを表示するタイプを作成しました。
プロンプトを表示するタイプ
インフォメーションを表示するタイプ
下のリンクになっているテキスト部分をブラウザのブックマークバーにドラッグアンドドロップして使用してください。
プロンプトを表示するタイプ:ブログカード
インフォメーションを表示するタイプ:ブログカード
インフォメーションを表示するタイプはClipboardを使っているのでHTTPSのみで動作します。Clipboard - Web API | MDN
※ブックマークレットの名前は、ドラッグアンドドロップ後ブックマークレットを右クリック「編集」から変更できます。
※要素の構成やclass名がThe other way roundさんのブログカードと同じものも作成しました。
class
すでにThe other way roundさんのブログカードをつかっている場合、CSSの変更は必要ないと思います。
記事後半にブックマークレット変換前のプログラムがあります。
2023年3月22日:インフォメーションを表示するタイプについて、2か所修正があります。
(記事内のコードは修正済みです)
修正前
/** インフォメーションの要素を非表示にする関数 */ function hideInfo() { const infoCreateBlogCard = document.getElementById("infoCreateBlogCard"); infoCreateBlogCard.style.display = "none"; } /** 指定した時間のあとにhideInfo関数を実行 */ setTimeout(hideInfo, 1000);
↓↓↓↓修正後
/** インフォメーションの要素を削除する関数 */ function removeInfo() { const infoCreateBlogCard = document.getElementById("infoCreateBlogCard"); infoCreateBlogCard.remove(); } /** 指定した時間のあとにremoveInfo関数を実行 */ setTimeout(removeInfo, 3000);
指定した時間の後に(ブログカードを作成しましたと表示される)インフォメーションの要素にdisplay:noneを設定して要素を非表示にしていました。
display:none
画面表示の上では問題ありませんが、ソースにインフォメーションの要素が残ってしまい、ちょっと行儀の悪い振る舞いだと感じました。
そこで指定した時間の後(初期値は3秒後)にインフォメーションの要素そのものを削除することにしました。
※(そもそも当初のコードではCSSで2.5秒かけて透過するアニメーションを設定、JavaScriptのsettimeoutで1秒後にdisplay:noneとしていた数値の設定ミスもあり、透過するアニメーションが終了する前に非表示となっていました)
settimeout
@media (prefers-color-scheme: dark) { .info-create-blogcard { background-color: #2b2a33; color: #fbfbfe; } }
@media (prefers-color-scheme: dark) { .info-create-blogcard { background-color: #2b2a33 !important; color: #fbfbfe !important; } }
OSがダークモードのときにCSSが効いていなかったので!importantを付けました。
!important
ダークモードの件はふじやんさん(ふじろじっく | ふじやん🌙(@fujiyanx)さん / Twitter)に教えてもらいました。ありがとうございます。
ブックマークレットとして使用する際は、ブックマークレット作成サイトなどでプログラムを変換してください。
ブックマークレット作成サイトの一例です。
サイトによってコメントを削除したり、スペースを%20に変換するなどの違いがあります。
%20
ブックマークレット作成
bookmarklet maker
ブックマークレット変換
プログラムのなかで、出力するHTMLを作成する部分はテンプレートリテラルで書きました。HTMLの構成やclass名などカスタマイズしやすいと思います。
以下はサムネイルがある場合のHTMLの構成です。
<figure class="blogcard"> <a class="blogcard__anchor" href="https://..." target="_blank" rel="noopener noreferrer" aria-label="記事詳細ページを別タブで開く" > <div class="blogcard__content-container"> <img class="blogcard__image" src="https://..." alt="タイトル" width="100" height="100" loading="lazy" /> <div class="blogcard__text-content-container"> <p class="blogcard__title">タイトル</p> <blockquote class="blogcard__blockquote" cite="https://..."> <p class="blogcard__description">ディスクリプション</p> </blockquote> </div> </div> <div class="blogcard__footer"> <img class="blogcard__footer-image" src="https://www.google.com/s2/favicons?domain=https://icooon-mono.com/" alt="ファビコン" width="16" height="16" loading="lazy" />ドメイン </div> </a> </figure>
(() => { /** * @see {@link https://vanillaice000.blog.fc2.com/blog-entry-1074.html} * @see {@link https://www.granfairs.com/blog/staff/blogcard-by-bookmarklet} */ /** データを格納 */ const objects = {}; /** metaタグを取得 */ const metaTags = document.getElementsByTagName("meta"); /** metaタグを繰り返し処理 */ for (const ogp of metaTags) { /** タイトルを取得 */ if (ogp.getAttribute("property") == "og:title") { objects.title = ogp.getAttribute("content"); /** サムネイルを取得 */ } else if (ogp.getAttribute("property") == "og:image") { objects.image = ogp.getAttribute("content"); /** ディスクリプションを取得 */ } else if (ogp.getAttribute("property") == "og:description") { objects.description = ogp.getAttribute("content"); } } /** OGPでデータを取得できなかったら */ if (objects.title == undefined) { objects.title = document.title; } if (objects.description == undefined) { for (const description of metaTags) { if (description.getAttribute("name") == "description") { objects.description = description.getAttribute("content"); } } } /** サイトのURLを取得 */ objects.url = document.URL; /** サイトのドメインを取得 */ objects.domain = location.host; /** 出力する要素を格納する変数 */ let blogCardBody; /** ブログカードのテキスト部分 */ const commonText = `<div class="blogcard__text-content-container"><p class="blogcard__title">${objects.title}</p><blockquote class="blogcard__blockquote" cite="${objects.url}"><p class="blogcard__description">${objects.description}</p></blockquote></div>`; /** ブログカードのフッター部分 */ const commonFooter = `<div class="blogcard__footer"><img class="blogcard__footer-image" src="https://www.google.com/s2/favicons?domain=${objects.url}" alt="ファビコン" width="16" height="16" loading="lazy"/>${objects.domain}</div>`; /** サムネイルの有無を判定 */ if (objects.image == undefined) { /** サムネイルがない場合 */ blogCardBody = `<figure class="blogcard blogcard--noimage"><a class="blogcard__anchor" href="${objects.url}" target="_blank" rel="noopener noreferrer" aria-label="%E8%A8%98%E4%BA%8B%E8%A9%B3%E7%B4%B0%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E5%88%A5%E3%82%BF%E3%83%96%E3%81%A7%E9%96%8B%E3%81%8F"><div class="blogcard__content-container">${commonText}</div>${commonFooter}</a></figure>`; } else { /** サムネイルがある場合 */ let thumnail = `<img class="blogcard__image" src="${objects.image}" alt="${objects.title}"width="100" height="100" loading="lazy"/>`; blogCardBody = `<figure class="blogcard"><a class="blogcard__anchor" href="${objects.url}" target="_blank" rel="noopener noreferrer" aria-label="%E8%A8%98%E4%BA%8B%E8%A9%B3%E7%B4%B0%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E5%88%A5%E3%82%BF%E3%83%96%E3%81%A7%E9%96%8B%E3%81%8F"><div class="blogcard__content-container">${thumnail}${commonText}</div>${commonFooter}</a></figure>`; } prompt( "%E2%86%93%E4%B8%8B%E3%81%AEHTML%E3%82%92%E3%82%B3%E3%83%94%E3%83%BC%E3%81%97%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84", blogCardBody ); })();
こちらはブックマークレットをクリックするだけでブログカードのHTMLがクリップボードにコピーされます。
(() => { /** * @see {@link https://vanillaice000.blog.fc2.com/blog-entry-1074.html} * @see {@link https://www.granfairs.com/blog/staff/blogcard-by-bookmarklet} */ /*! Copyright:2023 sutajp | The copyToClipboard function is: MIT license | https://sutasutashiki.blogspot.com/p/mit-license.html */ /** データを格納 */ const objects = {}; /** metaタグを取得 */ const metaTags = document.getElementsByTagName("meta"); /** metaタグを繰り返し処理 */ for (const ogp of metaTags) { /** タイトルを取得 */ if (ogp.getAttribute("property") == "og:title") { objects.title = ogp.getAttribute("content"); /** サムネイルを取得 */ } else if (ogp.getAttribute("property") == "og:image") { objects.image = ogp.getAttribute("content"); /** ディスクリプションを取得 */ } else if (ogp.getAttribute("property") == "og:description") { objects.description = ogp.getAttribute("content"); } } /** OGPでデータを取得できなかったら */ if (objects.title == undefined) { objects.title = document.title; } if (objects.description == undefined) { for (const description of metaTags) { if (description.getAttribute("name") == "description") { objects.description = description.getAttribute("content"); } } } /** サイトのURLを取得 */ objects.url = document.URL; /** サイトのドメインを取得 */ objects.domain = location.host; /** 出力する要素を格納する変数 */ let blogCardBody; /** ブログカードのテキスト部分 */ const commonText = ` <div class="blogcard__text-content-container"> <p class="blogcard__title">${objects.title}</p> <blockquote class="blogcard__blockquote" cite="${objects.url}"> <p class="blogcard__description">${objects.description}</p> </blockquote></div>`; /** ブログカードのフッター部分 */ const commonFooter = ` <div class="blogcard__footer"> <img class="blogcard__footer-image" src="https://www.google.com/s2/favicons?domain=${objects.url}" alt="ファビコン" width="16" height="16" loading="lazy"/> ${objects.domain}</div>`; /** サムネイルの有無を判定 */ if (objects.image == undefined) { /** サムネイルがない場合 */ blogCardBody = ` <figure class="blogcard blogcard--noimage"> <a class="blogcard__anchor" href="${objects.url}" target="_blank" rel="noopener noreferrer" aria-label="%E8%A8%98%E4%BA%8B%E8%A9%B3%E7%B4%B0%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E5%88%A5%E3%82%BF%E3%83%96%E3%81%A7%E9%96%8B%E3%81%8F"> <div class="blogcard__content-container">${commonText}</div> ${commonFooter}</a></figure>`; } else { /** サムネイルがある場合 */ let thumnail = ` <img class="blogcard__image" src="${objects.image}" alt="${objects.title}"width="100" height="100" loading="lazy"/>`; blogCardBody = ` <figure class="blogcard"> <a class="blogcard__anchor" href="${objects.url}" target="_blank" rel="noopener noreferrer" aria-label="%E8%A8%98%E4%BA%8B%E8%A9%B3%E7%B4%B0%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E5%88%A5%E3%82%BF%E3%83%96%E3%81%A7%E9%96%8B%E3%81%8F"> <div class="blogcard__content-container">${thumnail}${commonText}</div> ${commonFooter}</a></figure>`; } /** * クリップボードにコピーする関数 * @param {string} - blogCardBody * * Copyright:2023 sutajp * The copyToClipboard function is: MIT license * https://sutasutashiki.blogspot.com/p/mit-license.html */ function copyToClipboard(blogCardBody) { try { /** クリップボードにコピー */ navigator.clipboard.writeText(blogCardBody); /** インフォメーションの要素用CSS */ const style = ` <style> .info-create-blogcard { all: revert; font-family: "Helvetica Neue", Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", "BIZ UDPGothic", Meiryo, sans-serif; font-size: 16px; border: solid; border-radius: 10px; position: fixed; top: 30%; left: 50%; transform: translateX(-50%); background-color: #f9f9fb; color: #15141a; padding: 16px; z-index: 999; opacity: 0; animation: fadeOut 2.5s ease 0s 1 normal; } @keyframes fadeOut { 0% { opacity: 1; } 100% { opacity: 0; } } @media (prefers-color-scheme: dark) { .info-create-blogcard { background-color: #2b2a33; color: #fbfbfe; } } </style>`; /** インフォメーションの要素(テキスト) */ const infoElm = ` <div id="infoCreateBlogCard" class="info-create-blogcard"> ${style} ブログカードを作成しました</div>`; /** インフォメーションの要素をHTMLとしてbodyに挿入 */ document.body.insertAdjacentHTML("beforeend", infoElm); /** インフォメーションの要素を削除する関数 */ function removeInfo() { const infoCreateBlogCard = document.getElementById("infoCreateBlogCard"); infoCreateBlogCard.remove(); } /** 指定した時間のあとにremoveInfo関数を実行 */ setTimeout(removeInfo, 3000); } catch (error) { /** コピーに失敗したときのエラーハンドリング */ alert("コピーに失敗しました"); console.error("コピーに失敗しました"); return; } } copyToClipboard(blogCardBody); })();
CSSの一例です。
.blogcard { margin: auto; margin-block: 1.5em; padding: 0.5em; border: solid #656565; border-radius: 5px; box-shadow: 0 3px 5px rgb(0 0 0 / 50%); } .blogcard:hover { box-shadow: none; } .blogcard__anchor { color: rgb(51, 51, 51); text-decoration: none; } .blogcard__content-container { display: flex; } .blogcard__image { flex-grow: 1; width: 100px; height: 100px; margin-right: 1em; object-fit: cover; } .blogcard__text-content-container { flex-grow: 3; } /* タイトル, ディスクリプションが長い場合, 指定行以上を省略する https://coliss.com/articles/build-websites/operation/css/css-line-clamp-property.html */ .blogcard__title { margin: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .blogcard__blockquote { margin: 0; } .blogcard__blockquote::before, .blogcard__blockquote::after { content: none; } .blogcard__description { margin: 0.5em 0; font-size: 14px; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; } .blogcard__footer { font-size: 12px; text-align: end; } .blogcard__footer-image { margin-right: 5px; vertical-align: middle; }
以下は要素の構成やclass名がThe other way roundさんのブログカードと同じものです。
(() => { /** * @see {@link https://vanillaice000.blog.fc2.com/blog-entry-1074.html} * @see {@link https://www.granfairs.com/blog/staff/blogcard-by-bookmarklet} */ /** データを格納 */ const objects = {}; /** metaタグを取得 */ const metaTags = document.getElementsByTagName("meta"); /** metaタグを繰り返し処理 */ for (const ogp of metaTags) { /** タイトルを取得 */ if (ogp.getAttribute("property") == "og:title") { objects.title = ogp.getAttribute("content"); /** サムネイルを取得 */ } else if (ogp.getAttribute("property") == "og:image") { objects.image = ogp.getAttribute("content"); /** ディスクリプションを取得 */ } else if (ogp.getAttribute("property") == "og:description") { objects.description = ogp.getAttribute("content"); } } /** OGPでデータを取得できなかったら */ if (objects.title == undefined) { objects.title = document.title; } if (objects.description == undefined) { for (const description of metaTags) { if (description.getAttribute("name") == "description") { objects.description = description.getAttribute("content"); } } } /** サイトのURLを取得 */ objects.url = document.URL; /** サイトのドメインを取得 */ objects.domain = location.host; /** 出力する要素を格納する変数 */ let blogCardBody; /** ブログカードのテキスト部分 */ const commonText = `<div class="blogcard-text"><p class="blogcard-title">${objects.title}</p><blockquote cite="${objects.url}"><p class="blogcard-description">${objects.description}</p></blockquote></div>`; /** ブログカードのフッター部分 */ const commonFooter = `<div class="blogcard-footer"><img src="https://www.google.com/s2/favicons?domain=${objects.url}" alt="ファビコン" width="16" height="16" loading="lazy"/>${objects.domain}</div>`; /** サムネイルの有無を判定 */ if (objects.image == undefined) { /** サムネイルがない場合 */ blogCardBody = `<figure class="blogcard blogcard-hasnoimage"><a href="${objects.url}" target="_blank" rel="noopener noreferrer" aria-label="%E8%A8%98%E4%BA%8B%E8%A9%B3%E7%B4%B0%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E5%88%A5%E3%82%BF%E3%83%96%E3%81%A7%E9%96%8B%E3%81%8F"><div class="blogcard-content">${commonText}</div>${commonFooter}</a></figure>`; } else { /** サムネイルがある場合 */ let thumnail = `<div class="blogcard-image"><div class="blogcard-image-wrapper"><img src="${objects.image}" alt="${objects.title}"width="100" height="100" loading="lazy"/></div></div>`; blogCardBody = `<figure class="blogcard"><a href="${objects.url}" target="_blank" rel="noopener noreferrer" aria-label="%E8%A8%98%E4%BA%8B%E8%A9%B3%E7%B4%B0%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E5%88%A5%E3%82%BF%E3%83%96%E3%81%A7%E9%96%8B%E3%81%8F"><div class="blogcard-content">${thumnail}${commonText}</div>${commonFooter}</a></figure>`; } prompt( "%E2%86%93%E4%B8%8B%E3%81%AEHTML%E3%82%92%E3%82%B3%E3%83%94%E3%83%BC%E3%81%97%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84", blogCardBody ); })();
(() => { /** * @see {@link https://vanillaice000.blog.fc2.com/blog-entry-1074.html} * @see {@link https://www.granfairs.com/blog/staff/blogcard-by-bookmarklet} */ /*! Copyright:2023 sutajp | The copyToClipboard function is: MIT license | https://sutasutashiki.blogspot.com/p/mit-license.html */ /** データを格納 */ const objects = {}; /** metaタグを取得 */ const metaTags = document.getElementsByTagName("meta"); /** metaタグを繰り返し処理 */ for (const ogp of metaTags) { /** タイトルを取得 */ if (ogp.getAttribute("property") == "og:title") { objects.title = ogp.getAttribute("content"); /** サムネイルを取得 */ } else if (ogp.getAttribute("property") == "og:image") { objects.image = ogp.getAttribute("content"); /** ディスクリプションを取得 */ } else if (ogp.getAttribute("property") == "og:description") { objects.description = ogp.getAttribute("content"); } } /** OGPでデータを取得できなかったら */ if (objects.title == undefined) { objects.title = document.title; } if (objects.description == undefined) { for (const description of metaTags) { if (description.getAttribute("name") == "description") { objects.description = description.getAttribute("content"); } } } /** サイトのURLを取得 */ objects.url = document.URL; /** サイトのドメインを取得 */ objects.domain = location.host; /** 出力する要素を格納する変数 */ let blogCardBody; /** ブログカードのテキスト部分 */ const commonText = ` <div class="blogcard-text"> <p class="blogcard-title">${objects.title}</p> <blockquote cite="${objects.url}"> <p class="blogcard-description">${objects.description}</p> </blockquote></div>`; /** ブログカードのフッター部分 */ const commonFooter = ` <div class="blogcard-footer"> <img src="https://www.google.com/s2/favicons?domain=${objects.url}" alt="ファビコン" width="16" height="16" loading="lazy"/> ${objects.domain}</div>`; /** サムネイルの有無を判定 */ if (objects.image == undefined) { /** サムネイルがない場合 */ blogCardBody = ` <figure class="blogcard blogcard-hasnoimage"> <a href="${objects.url}" target="_blank" rel="noopener noreferrer" aria-label="%E8%A8%98%E4%BA%8B%E8%A9%B3%E7%B4%B0%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E5%88%A5%E3%82%BF%E3%83%96%E3%81%A7%E9%96%8B%E3%81%8F"> <div class="blogcard-content">${commonText}</div> ${commonFooter}</a></figure>`; } else { /** サムネイルがある場合 */ let thumnail = ` <div class="blogcard-image"> <div class="blogcard-image-wrapper"> <img src="${objects.image}" alt="${objects.title}"width="100" height="100" loading="lazy"/> </div></div>`; blogCardBody = ` <figure class="blogcard"><a href="${objects.url}" target="_blank" rel="noopener noreferrer" aria-label="%E8%A8%98%E4%BA%8B%E8%A9%B3%E7%B4%B0%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E5%88%A5%E3%82%BF%E3%83%96%E3%81%A7%E9%96%8B%E3%81%8F"> <div class="blogcard-content">${thumnail}${commonText}</div> ${commonFooter}</a></figure>`; } /** * クリップボードにコピーする関数 * @param {string} - blogCardBody * * Copyright:2023 sutajp * The copyToClipboard function is: MIT license * https://sutasutashiki.blogspot.com/p/mit-license.html */ function copyToClipboard(blogCardBody) { try { /** クリップボードにコピー */ navigator.clipboard.writeText(blogCardBody); /** インフォメーションの要素用CSS */ const style = ` <style> .info-create-blogcard { all: revert; font-family: "Helvetica Neue", Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", "BIZ UDPGothic", Meiryo, sans-serif; font-size: 16px; border: solid; border-radius: 10px; position: fixed; top: 30%; left: 50%; transform: translateX(-50%); background-color: #f9f9fb; color: #15141a; padding: 16px; z-index: 999; opacity: 0; animation: fadeOut 2.5s ease 0s 1 normal; } @keyframes fadeOut { 0% { opacity: 1; } 100% { opacity: 0; } } @media (prefers-color-scheme: dark) { .info-create-blogcard { background-color: #2b2a33; color: #fbfbfe; } } </style>`; /** インフォメーションの要素(テキスト) */ const infoElm = ` <div id="infoCreateBlogCard" class="info-create-blogcard"> ${style} ブログカードを作成しました</div>`; /** インフォメーションの要素をHTMLとしてbodyに挿入 */ document.body.insertAdjacentHTML("beforeend", infoElm); /** インフォメーションの要素を削除する関数 */ function removeInfo() { const infoCreateBlogCard = document.getElementById("infoCreateBlogCard"); infoCreateBlogCard.remove(); } /** 指定した時間のあとにremoveInfo関数を実行 */ setTimeout(removeInfo, 3000); } catch (error) { /** コピーに失敗したときのエラーハンドリング */ alert("コピーに失敗しました"); console.error("コピーに失敗しました"); return; } } copyToClipboard(blogCardBody); })();
.blogcard { margin: auto; margin-block: 1.5em; padding: 0.5em; border: solid #656565; border-radius: 5px; box-shadow: 0 3px 5px rgb(0 0 0 / 50%); } .blogcard:hover { box-shadow: none; } .blogcard > a { color: rgb(51, 51, 51); text-decoration: none; } .blogcard-content { display: flex; } .blogcard-image { flex-grow: 1; width: 100px; height: 100px; margin-right: 1em; object-fit: cover; } .blogcard-image-wrapper > img { object-fit: cover; } .blogcard-text { flex-grow: 3; } .blogcard-text > blockquote { margin: 0; } /* タイトル, ディスクリプションが長い場合, 指定行以上を省略する https://coliss.com/articles/build-websites/operation/css/css-line-clamp-property.html */ .blogcard-title { margin: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .blogcard-title > blockquote::before, .blogcard-title > blockquote::after { content: none; } .blogcard-description { margin: 0.5em 0; font-size: 14px; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; } .blogcard-footer { font-size: 12px; text-align: end; } .blogcard-footer > img { margin-right: 5px; vertical-align: middle; }
引用が必要なければblockquoteタグを削除してください。
blockquote
/** ブログカードのテキスト部分 */ const commonText = `<div class="blogcard__text-content-container"><p class="blogcard__title">${objects.title}</p><blockquote class="blogcard__blockquote" cite="${objects.url}"><p class="blogcard__description">${objects.description}</p></blockquote></div>`;
↓↓↓
/** ブログカードのテキスト部分 */ const commonText = `<div class="blogcard__text-content-container"><p class="blogcard__title">${objects.title}</p><p class="blogcard__description">${objects.description}</p></div>`;
プログラム内の日本語はパーセントエンコードしました。パーセントエンコード(Percent-encoding)
はてな風のブログカードをブックマークレットから作ってみよう! | 株式会社グランフェアズ
こんにちは、めぐたんです。 ブログを書いていると、参考記事や過去に書いた記事など別ページへのリンクを貼る機会が何かと多くあります。...
ブログカード作成ブックマークレットをアップデートしました
FC2ブログのみならず汎くお使い頂いているようで甲斐があったなぁ、と思っております ブログカード なんですが、アップデート、というか少しhtml内容を変更しました。...
サイト内検索に使ってください 🐤
© 2015 すたすた式
Enjoy!👍
QooQ
コメントなし:
コメントを投稿