スタイリング・フロントエンド豆知識集
主にはNext.js 14・Typescript・TailwindCSS・Storybook環境下で作業する時に、ちょっとした調べ物をしたり、軽くつまづいたことなどをメモしていきます。
需要がありそうなものなどは、そのうち別途記事に切り出します。
※そろそろ増えてきたのでTailwindCSSだけのスクラップに移行したい
svgにimg要素のalt的機能を持たせる
要約:svgにimg要素のalt的機能を持たせたい時は、
<svg role='img' aria-label='説明'></svg>
Safariだと100vhがうまく効かないとき:dvh(dynamic viewport height)を使う
.h-dvh
クラスがある(Safariで問題なく動作することを確認済み・現時点では他ブラウザ未確認🙏)
useState
を使わずにModalの開閉を管理する方法(searchParams
を使う)
useState
を使用するコンポーネントは'use client'
が必要になるが、そうしたくない場合に使えるテクニック
なお、headerなどには使えない(かもしれない)
Tailwind CSSの本が出る!🙌
.sr-only
というクラスの存在を知った…
表示はされないがスクリーンリーダーには認識される
Tail-animistaを使う
Tailwind CSSでアニメーションを楽につけるには:
SNSシェアボタン・リンクコピーの実装
Twitter(X)において、ハッシュタグを複数書きたい時は
hashtags=alpha,beta
のようにカンマで区切って書く
Twitter(X)のシェアURLについて、最新はshare?
ではなくintent/tweet?
となる
またLINEについて、こちらの情報によると
公式ドキュメントには記載されていないが、ブラウザからであれば、share?text=
でテキストも共有できるとのこと
Q「LINEで送る」ボタンでURLと一緒にテキストもシェアできますか?
カスタムアイコンを使って「LINEで送る」ボタンを作成する場合、URLと一緒に任意のテキストをシェアするよう設定できます。詳しくは、「カスタムアイコンを使用」を参照してください。LINE公式アイコンを使用する場合は、URLと一緒にテキストをシェアすることはできません。
FAQの作り方(未精査)
さっとみた限り、FAQ.jsでは<dl><dt><dd>を使ってQとAを書き分けるとよいのではないだろうか?
モバイルファーストでバナーを作成するときの適切な解像度の考え方
デザイン・ワイヤーだと実寸で作っているが、解像度を考えると幅・高さともに倍(=面積は4倍)くらいで書き出した方が良さそう
背景画像などと違い、画面に表示されるサイズが固定となるバナーの作成依頼をするために調べた
Next.js 13以降のImageの設定方法
TailwindCSSと組み合わせる場合、style部分はclassNameに置き換えてよいので、以下のように書くことができる。
<Image
src="/rocket.jpg"
alt="空飛ぶロケット"
width={1980}
height={1150}
sizes="100vw"
className={'w-full h-auto'} // styleを書き換え
/>
(layout="responsive"(サイズを可変にする画像)の場合から引用、className部分のみ書き換え)
画面幅に応じて表示文字数を調整したい時は、TailwindCSSのline-clampを使うと楽
Next.jsのサーバーコンポーネント上で、window.innerWidthなどを使用して画面サイズを取ろうとすると、windowオブジェクトはクライアントサイドでしか動作しないため、エラーとなってしまう。
しかし、文字列が長いときに折り返されるのを避けたいだけであれば、
TailwindCSSのline-clampを使用することで、動的な処理を一切書くことなく解決できる。
<p className='line-clamp-1'>Lorem ipsum dolor sit amet,</p>
と書くだけで、画面要素の幅より長い文字列は、その要素幅にあった長さに丸められ、末尾に「...」が付与される。
もちろん、line-clamp-3
と書けば3行の範囲内で丸められ、ブログ記事の冒頭をチラ見せするのに使用するなど、複数行表示するのに使う方が多いかもしれない。
縦方向に数行要素を並べた上で、更に横スクロールする時の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>にはこの要素を表示させない
- コード3行目:最初の5つの
-
古のMarqueeタグ
はHTML5で非推奨となった 【解決法①】 【解決法②】
TailwindCSS単体で作成できないグラデーションの実装・テキストグラデーション
こちらに記事を作成しました
Next.js 14でのfavicon設定方法
public
に配置して自力で<head/>
に記述するものが多いが、上記リンク先で書かれている通り、favicon
とapple-touch-icon
は/app
の中に置けばよい
しかしandroid-chrome
については記載がない
android-chrome
に関する仮説】
【- Next.jsでプロジェクトを作成したときに入っているデフォルトの
favicon.ico
には256 * 256も含まれていたので、favicon.ico
がchrome-android
もカバーしている? -
icon.png
として256 * 256のicon
を作成すればそれが適用される?
仮説を元に必要なファイル・画像サイズを洗い出す
種類 | ファイル名 | サイズ |
---|---|---|
favicon | favicon.ico | 16x16, 32x32, 256x256 |
apple-touch-icon | apple-icon.png | 180x180 |
chrome-android | icon.png | 256x256 |
検証結果
AndroidのChromeでページのタブアイコン、お気に入り登録した際のアイコン、ホーム画面に追加した際のアイコンを確認した結果、
- タブ ...
favicon.ico
が出たりicon.png
が出たりと結果がまちまち(同サイズだからか?) - お気に入り ...
icon.png
が表示される - ホーム画面 ... アイコン表示されず(URLの一文字目+Chromeアイコンに)
と必ずしもicon.png
が表示されるとは限らない結果に。
ただ、他サイトをホーム画面に追加してもアイコンが表示されるケースが確認できなかったため、ホーム画面に関しては別のところに原因がある可能性も。
またChromeで新規タブを開いた際に表示される、クイックアクセスにはicon.png
が表示されることを確認した。
Next.js 14で動的にOGP画像を生成する際のつまづきポイント
基本はこれに沿って行う
export const runtime = 'edge'
runtime
のデフォルトはnextjs
なので要注意
つまづきポイント0:プレビューしたい…!
でできる!
<img>
か<div>
で読み込む
つまづきポイント1:画像はいつもの習慣で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>
タグで回避
<div>
のdisplayはflex
かnone
しか選べない
つまづきポイント1.5:些細なことではあるが、意外とこれに触れている記事は少ない
flex
かnone
しか選べない時点で、必然的に<div style={{display: 'flex'}}>
となる
詳細はこちら
つまづきポイント2:日本語フォントの読み込み
こちらを参照して解決
つまづきポイント3:TailwindCSSで書ける、ただしtailwind.config.tsは効かないし、いくつか効かないクラスがある
truncate
、line-clamp-1
、object-contain
あたりが効かず、styleにcssで記載した
しかし、textOverflow: 'ellipsis'
は適切に効かず、...
が途切れてしまうことがあった
※また<h1>
などにはデフォルトで上下にmargin
が入っているので、m-0
などが必要
つまづきポイント4:ドメインの取得
app/middleware.ts
を作成し、ドメインを取得したい箇所(今回はopengraph-image.tsx
)に下記の要領で記載
(※上記例ではlayout.tsx
に記載しているが、layout.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>
)
)
}
脱線:意外とOGP画像のデザインについてまとめた記事が多くはなかったので、見つけた物を
やってみたい:
回転させたオブジェクトの位置指定(absolute, fixed)
TailwindCSSで書くと
<div className='fixed top-0 right-60px w-[100vh] h-60px -rotate-90 origin-top-right bg-white'>
縦横スクロールのスタイル調整(特に横)
横スクロールがあるデザインを行う時、ブラウザごとの横スクロールバーデザインの違い・OSによる表示/非表示の設定(Mac)によってレイアウトが崩れてしまうことがある
こちらの解決はTailwindCSSでは不可能
::-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;
}
画像の保存防止(Next.js)
これら2つを掛け合わせて、右クリック・ドラッグ&ドロップによる画像保存を行う方法として、以下を採用した
/* 画像保存防止 */
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/image
のImage
にoncontextmenu
、onselectstart
、onmousedown
の処理を書こうとしたが、エラーとなってしまった(client componentではないことによるものと、next/imageの仕様によるもの)ため、画像とその外側のdivを別途client componentとして書き出し、そちらに以下のように記載した
<div
onContextMenu={(e) => e.preventDefault()}
onSelect={(e) => e.preventDefault()}
onMouseDown={(e) => e.preventDefault()}
>
<Image />
</div>
Next.jsでスクロールポジションを保ちつつ画面遷移する方法
リンク遷移後のスクロールポジションを保ちたい時には、
< Link scroll={true} />
これだけでOK!!!
<ol>
の数字を(1)など自在にカスタマイズするには
規約とか書くのに役立った ... beforeに『第1章』など付けられる
CSSのcounter-incrementを使って簡単に自動で連番をつける方法
よさげなIdenticon3選
マイページなどにユーザーアイコンを設定したい、しかしユーザ情報はユーザ本人にしか表示されないので、ユーザが任意に画像を設定できる仕組みまで作るのはオーバーヘッド→Identiconライブラリを使おう!
【GitHub風Identicon】minidenticons
npm install minidenticons
【幾何学だけどかわいい】JDENTICON
npm install jdenticon
【ゆる顔アイコン・マーブル模様など6パターン】
npm install boring-avatars
こちらで試せる
minidenticonsとboring-avatarsを試したが、boring-avatarsの方が色味を設定できたり、useMemoが必要ないため、server componentでも使用できるなど、使い勝手は良い
ただ、6種類の見た目があるが、実際にアイコンとして特徴的な見た目が生成されるのは、beamとbauhausの2種類と感じた
popover APIを使おうとして断念した話:
- tailwindCSSはv4.0betaにてpopoverに対応していたが、アニメーション(opacityの変化)が制御できなかった
- 書き方の問題はありそう
- 階層が複雑なものだと、祖先クラスを見なければいけなかったり、クラス表記が汚くなっていく気配
- そもそもbetaなのでもう少し待っても良さそう
- そもそもモバイルファーストサイトのヘッダーメニューモーダルに使用しようとしたが、相性が悪い
- 全ての要素の上に表示されるので、元々はヘッダーとメインの間にモーダルを挟み込んでいたが、モーダルがヘッダーの上に来てしまい、ヘッダーを閉じるボタンも見えなくなってしまった
- モーダル内に閉じるボタンを書くことはもちろんできるが、冗長に思った
- 元々はjsを使ってリンク・ボタンがない領域をクリックしたらモーダルが閉じるようにしていたが、popoverでは「モーダル外」を押さないと閉じないため、モバイルでの全画面モーダルと相性が悪すぎた
- 全ての要素の上に表示されるので、元々はヘッダーとメインの間にモーダルを挟み込んでいたが、モーダルがヘッダーの上に来てしまい、ヘッダーを閉じるボタンも見えなくなってしまった
popoverAPIが悪いという話ではなく、適切なシーンではなかった、という話
popoverはこれが分かりやすい
TailwindCSSでの基本的な書き方TailwindCSSのarbitrary variants(ホチキスカッコで書けるアレ)のあれこれ
複数の画像が横スクロールし続ける、をTailwindCSSでやる:
(アクセシビリティ的には一時停止機能の追加がしたいところ)
(都合により没にしたので供養)
例はコーポレートサイトなどで良くみる、実績事例などで表示されている取引先企業ロゴが複数流れているイメージ
アニメーションのところは長くなっちゃうので別途CSSにまとめている
同じようなことを2回書いているのは、こうすることで途切れずにループするから
<div class="flex overflow-hidden w-full">
<ul class="flex flex-none list-none p-0 animate-infinity-scroll-left-1">
<li class="flex-none w-24 h-12 mr-2 bg-gray-500">企業ロゴ1</li>
<li class="flex-none w-24 h-12 mr-2 bg-gray-500">企業ロゴ2</li>
<li class="flex-none w-24 h-12 mr-2 bg-gray-500">企業ロゴ3</li>
<li class="flex-none w-24 h-12 mr-2 bg-gray-500">企業ロゴ4</li>
</ul>
<ul class="flex flex-none list-none p-0 animate-infinity-scroll-left-2">
<li class="flex-none w-24 h-12 mr-2 bg-gray-500">企業ロゴ1</li>
<li class="flex-none w-24 h-12 mr-2 bg-gray-500">企業ロゴ2</li>
<li class="flex-none w-24 h-12 mr-2 bg-gray-500">企業ロゴ3</li>
<li class="flex-none w-24 h-12 mr-2 bg-gray-500">企業ロゴ4</li>
</ul>
</div>
@keyframes infinity-scroll-left-1 {
0% {
transform: translateX(100%);
}
to {
transform: translateX(-100%);
}
}
@keyframes infinity-scroll-left-2 {
0% {
transform: translateX(0);
}
to {
transform: translateX(-200%);
}
}
.animate-infinity-scroll-left-1 {
animation: infinity-scroll-left-1 50s -25s linear infinite;
}
.animate-infinity-scroll-left-2 {
animation: infinity-scroll-left-2 50s linear infinite;
}
CSSだけでフッターをページ下部に張り付かせる方法:
別にjsとかを使わずとも、flex
の応用で可能だった
TailwindCSSで書くとこうなる:
<body class="h-full flex flex-col">
<header><h1>logo</h1></header>
<main class="flex-grow shrink-0 basis-auto">main</main>
<footer>footer</footer>
</body>
(元ページではhtml
にもheight: 100%
つまりh-full
を適用していたが、これはむしろ適用するとhtml全体の高さが画面の高さで固定されてしまうので不要と思われる)
ちなみに、モバイルファーストのように<body>
直下に<body>
的な機能を果たす囲いを作る場合、以下のようなコードで動作することを確認した
<body class="h-full"> ここから
<div class="flex flex-col max-w-md min-h-dvh mx-auto"> ここにを移し、min-h-dvhを足す
<header><h1>logo</h1></header>
<main class="flex-grow shrink-0 basis-auto">main</main>
<footer>footer</footer>
</div>
</body>
<body>
にあったflex flex-col
を1行下の<div>
に移し、さらにmin-h-dvh
を指定することで、コンテンツが少なくても100dvh
の高さを保証している