Open29

スタイリング・フロントエンド豆知識集

ピン留めされたアイテム
morit4ryomorit4ryo

主にはNext.js 14・Typescript・TailwindCSS・Storybook環境下で作業する時に、ちょっとした調べ物をしたり、軽くつまづいたことなどをメモしていきます。
需要がありそうなものなどは、そのうち別途記事に切り出します。

morit4ryomorit4ryo

svgにimg要素のalt的機能を持たせる

https://blog.nasbi.jp/programming/frontend/html/svg-alt/
要約:svgにimg要素のalt的機能を持たせたい時は、

<svg role='img' aria-label='説明'></svg>
morit4ryomorit4ryo

.sr-only(表示はされないがスクリーンリーダーには認識される)というTailwindCSSのクラスがあるので、用途によっては別途要素を作ってそちらにsr-onlyを適用する手がありそう

morit4ryomorit4ryo

逆(表示するがスクリーンリーダーに読ませない)のクラスはない(というかそもそもCSSで設定できる範疇を超えているはず)が、別途設定した上でaria-hiddenがtrueの時のみクラスを与えることができるaria-hidden:が存在する

morit4ryomorit4ryo
morit4ryomorit4ryo

Twitter(X)において、ハッシュタグを複数書きたい時は

hashtags=alpha,beta

のようにカンマで区切って書く

morit4ryomorit4ryo

Twitter(X)のシェアURLについて、最新はshare?ではなくintent/tweet?となる
またLINEについて、こちらの情報によると
https://kojole.hatenablog.com/entry/2018/09/19/113840
公式ドキュメントには記載されていないが、ブラウザからであれば、share?text=でテキストも共有できるとのこと
https://developers.line.biz/ja/faq/tags/sp-share/#is-it-possible-to-share-text-with-url

Q「LINEで送る」ボタンでURLと一緒にテキストもシェアできますか?
カスタムアイコンを使って「LINEで送る」ボタンを作成する場合、URLと一緒に任意のテキストをシェアするよう設定できます。詳しくは、「カスタムアイコンを使用」を参照してください。LINE公式アイコンを使用する場合は、URLと一緒にテキストをシェアすることはできません。

morit4ryomorit4ryo

モバイルファーストでバナーを作成するときの適切な解像度の考え方

https://bindup.jp/camp/design/graphic/14927

デザイン・ワイヤーだと実寸で作っているが、解像度を考えると幅・高さともに倍(=面積は4倍)くらいで書き出した方が良さそう
背景画像などと違い、画面に表示されるサイズが固定となるバナーの作成依頼をするために調べた

morit4ryomorit4ryo

Next.js 13以降のImageの設定方法

https://ebisu.com/note/next-image-migration/

TailwindCSSと組み合わせる場合、style部分はclassNameに置き換えてよいので、以下のように書くことができる。

<Image
  src="/rocket.jpg"
  alt="空飛ぶロケット"
  width={1980}
  height={1150}
  sizes="100vw"
  className={'w-full h-auto'} // styleを書き換え
/>

layout="responsive"(サイズを可変にする画像)の場合から引用、className部分のみ書き換え)

morit4ryomorit4ryo

画面幅に応じて表示文字数を調整したい時は、TailwindCSSのline-clampを使うと楽

Next.jsのサーバーコンポーネント上で、window.innerWidthなどを使用して画面サイズを取ろうとすると、windowオブジェクトはクライアントサイドでしか動作しないため、エラーとなってしまう。
しかし、文字列が長いときに折り返されるのを避けたいだけであれば、
TailwindCSSのline-clampを使用することで、動的な処理を一切書くことなく解決できる。
https://tailwindcss.com/docs/line-clamp

<p className='line-clamp-1'>Lorem ipsum dolor sit amet,</p>

と書くだけで、画面要素の幅より長い文字列は、その要素幅にあった長さに丸められ、末尾に「...」が付与される。
もちろん、line-clamp-3と書けば3行の範囲内で丸められ、ブログ記事の冒頭をチラ見せするのに使用するなど、複数行表示するのに使う方が多いかもしれない。

morit4ryomorit4ryo

縦方向に数行要素を並べた上で、更に横スクロールする時のTailwindCSSのクラス

<ul className='flex flex-col overflow-x-auto flex-wrap max-w-430px h-[390px]'>
  {items.map((item: any, index: number) => (
    <li key={index} className={index < 5 ? 'mx-5' : 'mr-5'}>
      <div className={w-[calc(100vw_-_40px)] max-w-[370px] h-[70px]}>省略</div>
      <div
        className={`w-[calc(100vw_-_40px)] max-w-[370px] h-1px bg-gray-300 mb-[9px] ${
        index % 5 === 4 || index === items.length - 1 ? 'hidden' : ''
        }`}
      />
    </li>
  ))}
</ul>

ポイント

  • <ul>内のTailwindCSSクラス
    • flex-colで縦方向に並べる
    • overflow-x-autoで横方向にスクロール
    • flex-wrap h-[390px] で、390pxを超えたら折り返して並べるようにしている(390pxはliを5つ並べた時の高さが80px * 4 + 70px = 390pxのため)
    • max-w-430px で表示幅をトリミング
  • <li>内のTailwindCSSクラス
    • items.lengthを活用し、状況に応じたクラスの使い分け
      • コード3行目:最初の5つの<li>だけは左右にマージンをつけたい、それ以外は右側にだけつけたい
      • 7行目:縦に<li>を5行ならべた際の5つ目と最後の<li>にはこの要素を表示させない
morit4ryomorit4ryo

Next.js 14でのfavicon設定方法

https://nextjs.org/docs/app/api-reference/file-conventions/metadata/app-icons
古いVer.のNext.jsでの記事では他の画像と同じように、publicに配置して自力で<head/>に記述するものが多いが、上記リンク先で書かれている通り、faviconapple-touch-icon/appの中に置けばよい
しかしandroid-chromeについては記載がない

android-chromeに関する仮説】

  1. Next.jsでプロジェクトを作成したときに入っているデフォルトのfavicon.icoには256 * 256も含まれていたので、favicon.icochrome-androidもカバーしている?
  2. icon.pngとして256 * 256のiconを作成すればそれが適用される?

仮説を元に必要なファイル・画像サイズを洗い出す

種類 ファイル名 サイズ
favicon favicon.ico 16x16, 32x32, 256x256
apple-touch-icon apple-icon.png 180x180
chrome-android icon.png 256x256

検証結果

https://libre-co.com/markup/favicon-howto/#sp(androidios)
を参考に、
AndroidのChromeでページのタブアイコン、お気に入り登録した際のアイコン、ホーム画面に追加した際のアイコンを確認した結果、

  • タブ ... favicon.icoが出たりicon.pngが出たりと結果がまちまち(同サイズだからか?)
  • お気に入り ... icon.pngが表示される
  • ホーム画面 ... アイコン表示されず(URLの一文字目+Chromeアイコンに)

と必ずしもicon.pngが表示されるとは限らない結果に。
ただ、他サイトをホーム画面に追加してもアイコンが表示されるケースが確認できなかったため、ホーム画面に関しては別のところに原因がある可能性も。
またChromeで新規タブを開いた際に表示される、クイックアクセスにはicon.pngが表示されることを確認した。

morit4ryomorit4ryo

Next.js 14で動的にOGP画像を生成する際のつまづきポイント

https://nextjs.org/docs/app/api-reference/file-conventions/metadata/opengraph-image
基本はこれに沿って行う

export const runtime = 'edge'

runtimeのデフォルトはnextjsなので要注意


つまづきポイント0:プレビューしたい…!

https://og-playground.vercel.app/
でできる!


つまづきポイント1:画像は<img><div>で読み込む

いつもの習慣でimport Image from 'next/image'しようとしてしまったが、
(正確にはfunction Image()とぶつかるため、import NextImage from 'next/image'

Error: failed to pipe response
Cannot access Image.toString on the server. You cannot dot into a client module from a server component. You can only pass the imported name through.

というエラーになる
やりたいことは画像を読み込むだけなので、今回は<img>タグで回避

つまづきポイント1.5:<div>のdisplayはflexnoneしか選べない

些細なことではあるが、意外とこれに触れている記事は少ない
flexnoneしか選べない時点で、必然的に<div style={{display: 'flex'}}>となる
詳細はこちら
https://github.com/vercel/satori?tab=readme-ov-file#css


つまづきポイント2:日本語フォントの読み込み

https://zenn.dev/temasaguru/articles/2968736b5a2f41#フォントファイルの読み込みについて
こちらを参照して解決


つまづきポイント3:TailwindCSSで書ける、ただしtailwind.config.tsは効かないし、いくつか効かないクラスがある

https://vercel.com/blog/introducing-vercel-og-image-generation-fast-dynamic-social-card-images
実際に確認できたものとしては、truncateline-clamp-1object-containあたりが効かず、styleにcssで記載した
しかし、textOverflow: 'ellipsis'は適切に効かず、...が途切れてしまうことがあった

※また<h1>などにはデフォルトで上下にmarginが入っているので、m-0などが必要


つまづきポイント4:ドメインの取得

https://qiita.com/P-man_Brown/items/3d0e0ad09db568848367
上記を参照してapp/middleware.tsを作成し、ドメインを取得したい箇所(今回はopengraph-image.tsx)に下記の要領で記載
(※上記例ではlayout.tsxに記載しているが、layout.tsxに書かないといけないわけではない)

app/***/[slug]/opengraph-image.tsx
/* eslint-disable @next/next/no-img-element */
import { ImageResponse } from 'next/og'
import { headers } from 'next/headers'

// (中略)

export default async function Image({ params }: { params: { slug: string } }) {
  const header_url = headers().get('x-url') || ''
  const url = new URL(header_url)
  const hostName = url.host // ドメイン名(ホスト名)を取得
  const protocol = url.protocol // プロトコルを取得 ('https:' または 'http:')
  return new ImageResponse(
    (
      <div>
        <img
          src={`${protocol}//${hostName}/ogp.png`}
          alt={''}
        />
      </div>
    )
  )
}
morit4ryomorit4ryo

縦横スクロールのスタイル調整(特に横)

横スクロールがあるデザインを行う時、ブラウザごとの横スクロールバーデザインの違い・OSによる表示/非表示の設定(Mac)によってレイアウトが崩れてしまうことがある
こちらの解決はTailwindCSSでは不可能

globals.css
::-webkit-scrollbar {
  width: 10px;
  /* ↓ 横スクロールバー用にheightが必要 */
  height: 10px;
  background-color: #f7f7f7;
}

::-webkit-scrollbar-thumb {
  background-color: #c9c9c9;
  /* ↓ あまり理想的な形状にはならないため、思い切って省くのもアリでは */
   border-radius: 8px;
}

/* 細かく指定するのは面倒なのでhtmlに直で適用する(bodyだと適用できない)*/
html {
  scrollbar-color: #c9c9c9 #f7f7f7;
  /* ↓ 横スクロールバーにも適用される様子 */
  scrollbar-width: auto;
}
morit4ryomorit4ryo

画像の保存防止(Next.js)

https://kakechimaru.com/img_dl_ban/
https://office-obata.com/report/memorandum/post-6035/
これら2つを掛け合わせて、右クリック・ドラッグ&ドロップによる画像保存を行う方法として、以下を採用した

globals.css
/* 画像保存防止 */
img {
  /* PCの右クリック禁止 */
  pointer-events: none;
  /* SPの長押し禁止 */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -moz-touch-callout: none;
  -moz-user-select: none;
  user-select: none;
}

なお、layout.tsx<body />next/imageImageoncontextmenuonselectstartonmousedownの処理を書こうとしたが、エラーとなってしまった(client componentではないことによるものと、next/imageの仕様によるもの)ため、画像とその外側のdivを別途client componentとして書き出し、そちらに以下のように記載した

<div
    onContextMenu={(e) => e.preventDefault()}
    onSelect={(e) => e.preventDefault()}
    onMouseDown={(e) => e.preventDefault()}
>
    <Image />
</div>
morit4ryomorit4ryo

よさげなIdenticon3選

マイページなどにユーザーアイコンを設定したい、しかしユーザ情報はユーザ本人にしか表示されないので、ユーザが任意に画像を設定できる仕組みまで作るのはオーバーヘッド→Identiconライブラリを使おう!
【GitHub風Identicon】minidenticons
https://github.com/laurentpayot/minidenticons

npm install minidenticons

【幾何学だけどかわいい】JDENTICON
https://jdenticon.com/

npm install jdenticon

【ゆる顔アイコン・マーブル模様など6パターン】
https://github.com/boringdesigners/boring-avatars

npm install boring-avatars

こちらで試せる
https://boringavatars.com/516d7d-2a728e-9d870c-f93f03-f9eee2

minidenticonsとboring-avatarsを試したが、boring-avatarsの方が色味を設定できたり、useMemoが必要ないため、server componentでも使用できるなど、使い勝手は良い
ただ、6種類の見た目があるが、実際にアイコンとして特徴的な見た目が生成されるのは、beamとbauhausの2種類と感じた