メインコンテンツへ
Onbit

2026.09.02

Web 名刺を作ったら、いちばん使われる場所で動いていなかった

技術 Web制作

はじめに

紙の名刺の裏面を、そのまま Web ページに起こした。URL を 1 本渡せば自己紹介が終わるようにしたかったからで、電話帳に登録できる vCard も一緒に置いた。

作り終えて実機で触ったら、いちばん使われるはずの場所で動いていなかった。LINE で URL を送って、相手が LINE の中で開く。この経路でだけ「連絡先を保存する」が無言で何も起きない。エラーも出ない。押しても何も変わらないので、壊れていることにすら気づきにくい。

Web 名刺そのものより、この手の「特定の環境でだけ静かに死ぬ」話のほうが役に立つと思うので、そこを中心に書きます。

基礎となる知識

  • vCard: 連絡先を 1 ファイルにまとめた標準フォーマット。拡張子は .vcf。スマホで開くと「新規連絡先を作成」の画面が出る
  • Content-Type: サーバがファイルを返すときに付ける「これは何のファイルか」という札。ブラウザはこれを見て開き方を決める
  • アプリ内ブラウザ: LINE や X などのアプリの中で開く簡易ブラウザ。Chrome や Safari とは別物で、できることが制限されている
  • コントラスト比: 文字色と背景色の明るさの差。WCAG(Web アクセシビリティの国際的な指針)の AA 基準は 4.5:1

解説と使い方

共通レイアウトを使わないという判断

最初に決めたのは、サイトの共通レイアウトを使わないことだった。ヘッダー・フッター・画面下に固定されるナビゲーションが必ず描画されるので、名刺として渡した URL が「サイトの中の 1 ページ」に見えてしまう。下端のナビゲーションは連絡ボタンとも場所を取り合う。

名刺は名刺として完結していてほしかったので、このページだけ自己完結の HTML にした。サイト全体の統一を崩す判断なので迷ったけれど、渡した相手が最初に見る 1 画面が名刺に見えるかどうかのほうが大事だと思った。

vCard は手書きの静的ファイルにしない

最初は public/onbit-card.vcf に手で書いたファイルを置いていた。これが品質チェックで引っかかった。電話番号がサイト設定ファイルと二重管理になっていて、片方を差し替えるともう片方に古い番号が残る。しかも残るのは相手の電話帳の中なので、間違いに気づく手段が無い。

生成する形に変えた。Astro なら 1 ファイルで済む。

// src/pages/onbit-card.vcf.ts
export const GET: APIRoute = () =>
  new Response(
    // vCard は CRLF 改行が仕様 (RFC 6350)
    LINES.join("\r\n") + "\r\n",
    { headers: { "Content-Type": "text/vcard; charset=utf-8" } },
  );

配信 URL は静的ファイルの頃と同じにしたので、既に配った URL や設定はそのまま生きる。連絡先は連絡先の設定ファイル、氏名と肩書は新しく作ったプロフィールの設定ファイル、と出所を 1 か所ずつに固定した。

改行は CRLF、ふりがなは OS で読む場所が違う

vCard で地味に効く仕様が 2 つあった。

1 つは改行が CRLFだということ(RFC 6350)。LF だけでも読める端末はあるけれど、取り込みが安定しない。

もう 1 つがふりがなで、iOS と Android で読むプロパティが違う。両方書いておく。

X-PHONETIC-LAST-NAME:苗字
X-PHONETIC-FIRST-NAME:名前
SOUND;X-IRMC-N:苗字;名前;;;

住所は入れていない。所在地は請求書や契約書には書くけれど、Web には出さない方針にしているので、そこは揃えた。

Content-Type を上書きする

.vcf に対して静的ホスティングが自動で付ける Content-Type は、古い text/x-vcard になることがある。今の標準は text/vcard なので、配信側で上書きした。Cloudflare Pages なら _headers に書く。

/onbit-card.vcf
  Content-Type: text/vcard; charset=utf-8
  X-Robots-Tag: noindex

ここは思い込みで進めず、wrangler pages dev でローカルに本番同等のヘッダを再現して、実際に置き換わることを確かめてから進めた。ヘッダは以前に一度、同名のヘッダが結合されてページが真っ白になる事故を起こしているので、確認するようにしている。

LINE のアプリ内ブラウザで無言で死ぬ

そして本題。Android の実機で LINE から開いて「連絡先を保存する」を押したら、何も起きなかった。押した感触もエラーも無い。LINE のアプリ内ブラウザは vCard のようなファイルの導線を扱えず、黙って捨てる。

このページは「LINE で URL を送って、相手が LINE の中で開く」のが本来の使われ方なので、そこで動かないなら機能していないのと同じだった。Chrome で開発している限り 100% 再現しないのも厄介なところ。

解決は 1 行だった。LINE には、リンクを外部ブラウザで開かせるためのクエリパラメータがある。

/onbit-card.vcf?openExternalBrowser=1

これを vCard のリンクにだけ付けた。ページ本体は LINE の中で読んだまま、保存の瞬間だけ Chrome や Safari に出る。通常のブラウザでは無視される単なるクエリなので、副作用も無い。

ページ全体を外部ブラウザに逃がすこともできるけど、それだと相手を LINE から追い出すことになる。壊れている 1 つの導線だけを逃がすのがちょうどいいと思う。

download 属性は付けないほうがいい

「保存」なのだから、と download 属性を付けていた。これは外した。iOS の Safari は download を尊重してファイルとして保存してしまい、「新規連絡先を作成」の画面が出ない。連絡先アプリに渡したいなら、素直にリンクとして開かせるほうがいい。

なおこの挙動は現時点で私の手元の iPhone では未確認で、直したのは仕様上の理屈と Android 側での確認まで。ここは正直に書いておきます。

紙の色をそのまま画面に持ってくると落ちる

紙の名刺で使っている金色(#b08a37)を、そのままクリーム色の背景に載せたらコントラスト比が 3.0:1 しかなかった。AA 基準の 4.5:1 に届かない。

紙は反射で読めるけれど、画面はそうはいかない。文字用の金だけ暗くする(#8a6a25 / 4.74:1)ことにして、罫線や枠や飾りのドットは紙と同じ金のままにした。紙の印象は保ったまま、文字だけ読めるようにする、という分け方になる。透明度 0.55 で薄くしていたラベルも 3.4:1 しかなかったので 0.78 まで戻した。

やってみた結果

https://onbit.jp/card/ として公開して、本番の配信も確認できている(vCard は Content-Type が text/vcard; charset=utf-8 で返っている)。URL を手渡しする相手向けのページなので、検索には出さない設定(noindex)にして、サイトマップからも外した。

作ってみて気づいたのは、名刺の Web 版は「作る」より「渡し方」で決まるということだった。デザインを紙と揃えるところは楽しい作業だけれど、成果が変わったのは ?openExternalBrowser=1 の 1 行のほうだった。

もう 1 つ。動作確認の環境が Chrome だけだと、この不具合は永遠に見つからない。LINE の中で見られるページは思っているより多くて、顧客サイトでも同じことが起きる。実機の LINE で開いてみる、というのを手順に足しました。

紙を刷り直したら Web も直す、という運用の紐付けも文書に書いた。ここは仕組みで縛らないと、確実に片方だけ古くなると思っている。

まとめ

  • vCard は生成する。静的ファイルに手で書くと連絡先が二重管理になり、間違いは相手の電話帳の中で腐る
  • 改行は CRLF。ふりがなは iOS と Android の両方のプロパティを書く
  • .vcf の Content-Type は text/vcard に上書きする。上書きが効いているかはローカルで本番同等の環境を立てて確認する
  • LINE のアプリ内ブラウザはファイルの導線を黙って捨てる。?openExternalBrowser=1壊れている導線にだけ付ける
  • download 属性は付けない。連絡先アプリに渡らなくなる
  • 紙の色をそのまま画面に持ってくるとコントラストが足りない。文字用の色を分ける

参考文献

let's talk

気軽に、お話を聞かせてください

30 分の無料相談です。教室・宿・治療院・小さな店、どんな業種でもまずは聞かせてください。「まだぼんやりしていて」も大丈夫。合わなければ、そっと離れていただいて構いません。