Astroで画像のホバーCSSが効かなかった原因

Astroで画像のホバーCSSが効かなかった原因

目次

このブログを直している途中で、「ホバー時の画像のCSS変えた?」と聞いたことがある。

記事カードの画像は、普段は少し色を落とし、マウスを重ねると元の色に戻るようにしている。その表示を直した履歴を読むと、変更したのはCSSではなく、画像コンポーネントの属性の受け渡しだった。

CSSの公式ロゴ。紫色の背景に白いCSSの文字。

画像:CSS-Nextの公式ロゴ。ロゴの色と形を保ち、掲載用に余白を付けています。

画像を表示する部品を挟んでいた#

カード内の画像は、AstroのImageを直接置く代わりに、PostImage.astroを通している。

ControlPanelCard.astro
  └─ PostImage.astro
       └─ Image → img

この部品には、縦長の画像を一定の高さに収める役割もある。本の表紙を最後まで見せるための調整を、使う場所ごとに書かずに済む。

一方、画像を灰色に近づける指定は、呼び出し元のカード側にある。通常はgrayscale(0.8)、ホバー時はgrayscale(0)。変化にかける時間は0.3秒だ。

画像自体は表示できていても、呼び出し元からスタイルが届くかは、別に確認する必要があった。

CSSの対象は、単なるimgではない#

Astroのコンポーネント内に書いた通常のstyleは、そのコンポーネントの範囲に適用される。

このサイトの既定の方式では、生成されたHTMLへdata-astro-cid-…という属性が付き、CSS側にも対応する条件が加わる。説明用に短く書くと、次のような関係になる。

<img data-astro-cid-example />
img[data-astro-cid-example] {
  filter: grayscale(0.8);
}

exampleは説明用の名前で、実際の識別子ではない。

この仕組みによって、ほかの場所の画像まで同じ指定が広がることを避けられる。ただ、コンポーネントを一つ挟んだときに、この属性を途中で落とすと、最終的なimgがCSSの条件に合わなくなる。

Astroの公式ドキュメントにも、既定のスコープ方式では、この属性を子へ渡す必要があると説明されている。

修正したのは、受け取った属性の行き先#

修正前のPostImageは、Astro.propsから使う値だけを取り出していた。

const { src, alt, portraitMaxHeight = 128 } = Astro.props;

修正後は、それ以外の属性も受け取る形になった。

const { src, alt, portraitMaxHeight = 128, ...attributes } = Astro.props;

さらに、そのattributesを内側のImageへ渡している。実際のテンプレートからの抜粋は次のとおり。

<Image
  {...attributes}
  src={src}
  alt={alt}
  class:list={{ portrait: src.height > src.width }}
  style={`--portrait-max-height: ${portraitMaxHeight}px`}
/>

この変更は、9月17日の修正コミットに残っている。色やアニメーションの指定を変えたのではなく、親から受け取ったスコープ属性を画像へ届かせる変更だった。

なお、この部品はclassstyleを自身でも指定している。今回の修正だけで、呼び出し元から来るあらゆるclassやstyleが自動的に合成されるわけではない。汎用的な画像部品へ広げるなら、その扱いも決めたい。

同じ「効かない」でも、確かめる場所が違う#

今回のような症状なら、次の順に見ると原因を絞りやすい。

  1. CSSのルールが生成されているか。
  2. 画像のHTMLに、そのルールが必要とする属性があるか。
  3. 別のルールで上書きされていないか。
  4. ホバー用のメディア条件を満たしているか。

このサイトのホバー処理にはany-hover: hoverという条件がある。ホバーできる入力機器がない環境では、そのルール自体が適用されない。PCで属性を確認することと、タッチ操作で表示を見ることは分けて考える。

CSSの優先度を上げる前に、そもそも対象の要素が条件に合っているかを見る。今回の差分は、その確認の大切さを説明できる小さな例になった。

共通化した部品の、境目を見る#

srcaltが渡れば、画像は表示できる。でも、親のスタイルが届くためには、それ以外の属性も必要だった。

共通コンポーネントを作るときは、その部品が自分で使う値と、内側へ通す値を整理する。見た目の不具合でも、原因はCSSファイルの外にあることがある。

この修正はCodexと進めた。今回は修正履歴と現在のHTML・CSSを読み返し、どこで属性が止まっていたのかを整理した。

Related Post
「新しい順」だけでは足りなかった。記事の並び順を考える
image

このブログで、グリッチハンターの記事をピン留めしてほしいと頼んだあと、「先頭になってない」と伝えたことがある。

記事のデータにピン留めの印を付けることと、画面の先頭へ並べることは、別の処理だった。記事を増やしながら、何を基準に並べるかも決める必要が出てきた。

Meta Data
公開日:2026-09-19
Tags:
#個人開発
#tech
投稿と再現手順を、まとめて保存する。グリッチハンターのDB設計
image

ゲームのバグを投稿するアプリには、タイトルや説明だけでなく、「どう操作すると、その現象が起きるか」という再現手順も必要になる。

Glitch Hunter Libraryのコードを読み返すと、投稿と手順を別のテーブルに保存し、両方の書き込みを一つのトランザクションにまとめていた。今回は、その実装をたどってみる。

Meta Data
公開日:2026-09-18
Tags:
#個人開発
#tech
Astroブログの下書きを、一覧・RSS・サイトマップからも除外する
image

このブログは、AIと下書きを作り、ローカルで読んでから公開している。下書きは手元で確認したい。でも、本番には出したくない。

そのために記事へ付けているのが、draft: trueだ。今回整理したのは、この値を記事ページだけでなく、一覧やRSSなどの出力にも反映する処理だった。

Meta Data
公開日:2026-09-18
Tags:
#個人開発
#tech
AstroブログのSEOを整えた。メタデータとSearch Consoleで確認したこと
image

このAstroブログのSEO周りを、Codexと一緒に整えた。canonical、OGP、記事の構造化データを確認し、Google Search Consoleも設定した。

その中で気になったのが、「memida」と検索するとContactが上の方に出てくることだった。ページを直したことと、検索結果が変わったことを混同しないように、作業と確認結果を分けて残しておく。

Meta Data
公開日:2026-09-18
Tags:
#個人開発
#tech