【2025年版】知っておきたいフロントのあれこれ

もう2025年が始まって2ヶ月も経ったなんて信じられないほど、時間が経つのがめちゃくちゃ早く感じます...
もうこれ以上何を進化するんだと思うほど進化をし続けてきたフロントエンドのトレンドも、まだまだ移り変わっていて追いつくのも精一杯です...

今回は個人的に超便利なものたちをまとめましたのでぜひ最後までご覧ください！

# HTMLのあれこれ
まずはHTMLの便利要素を紹介していきます。
もうすでに知っている方が多い要素も紹介しますが、便利なので載せておきます！

## picture要素
picture要素は、画面サイズや解像度、サポートされている画像形式などさまざまなシチュエーションで出力する画像を自動で切り替えることができます。

何が便利なのかというと、基本的なビューポートによって画像を分けるなどはCSSで`display`を`none`にしたりすると思うのですが、
`picture`タグとして画像をまとめると条件に当てはまっていない画像は読み込まれなくなります。

そのため、一度読み込んでから非表示にする`display: none`とは異なり、読み込みが早くなるというメリットがあります。

---
### 基本的な使い方
```html
<picture>
  <source srcset="image-small.jpg" media="(max-width: 600px)">
  <source srcset="image-large.jpg" media="(min-width: 601px)">
  <img src="default-image.jpg" alt="説明文">
</picture>
```
`source`タグの`media`属性に指定したシチュエーションに当てはまっていれば`srcset`の画像を出力します。
`img`タグにHTML5をサポートしていない場合の初期値を指定します。

それらをまとめるのが`picture`タグです。

### 解像度によって分ける
```html
<picture>
  <source srcset="image@2x.jpg" media="(min-resolution: 192dpi)">
  <img src="image.jpg" alt="画像説明">
</picture>
```
この場合、デバイスの解像度が192dpi以上の場合に`image@2x.jpg`が表示され、
それ以外の場合は`image.jpg`が表示されます。

### ビューポートによって分ける
```html
<picture>
  <source srcset="image-mobile.jpg" media="(max-width: 600px)">
  <source srcset="image-desktop.jpg" media="(min-width: 601px)">
  <img src="default-image.jpg" alt="画像説明">
</picture>
```
この場合、デバイスの画面幅が600px以下の場合は`image-mobile.jpg`が、
601px以上の場合は`image-desktop.jpg`が表示されます。

### 画像形式によって分ける
```html
<picture>
  <source srcset="image.webp" type="image/webp">
  <source srcset="image.jpg" type="image/jpeg">
  <img src="image.jpg" alt="画像説明">
</picture>
```
`webp`は品質を保ちつつファイルサイズが小さくなるため、可能な限り使用したいものです。
この場合、ブラウザ側で`webp`がサポートされていれば`image.webp`が使用され、そうでなければ`image.jpg`が使用されます。

## dialog要素
dialog要素は、モーダルやポップアップ要素を作成するときに便利なものです。
この中に書かれている要素は自動的に`z-index`の指定がなくとも画面トップに表示される仕様となっているため、独自でダイアログを作りたい際に重宝します。

また、開く/閉じる の処理をCSSを用いず簡単に実装できるのもありがたい部分です。

---

### 基本的な使い方
```html
<dialog id="dialog">
  <p>これはダイアログボックスの内容です。</p>
  <button id="closeBtn">閉じる</button>
</dialog>
<button id="openBtn">ダイアログを開く</button>

<script>
  const openButton = document.getElementById('openBtn');
  const closeButton = document.getElementById('closeBtn');
  const dialog = document.getElementById("dialog");

  openButton.addEventListener('click', () => {
    dialog.showModal();  // ダイアログをモーダルで表示
  });
  
  closeButton.addEventListener('click', () => {
    dialog.close();  // ダイアログを閉じる
  });

  // モーダル以外の場所をクリックしたら閉じる
  dialog.addEventListener("click", (event) => {
    if (event.target === dialog) {  // クリックされた要素が dialog 自体（背景）
      dialog.close();
    }
  });
</script>
```
`dialog`・・・ダイアログのメイン部分を記述します。これらは通常状態では表示されなくなっています。
`dialog.showModal()`・・・モーダルとして`dialog`要素が表示されます。
`dialog.close()`・・・開いていたモーダルが非表示になります。

:::note info
### 補足機能

`dialog.show()`を実行すると、通常ダイアログとして`dialog`要素が表示されます。
モーダル状態と異なり、他の要素のインタラクションをブロックすることがない状態になります。

`open`属性を`dialog`要素につけると、初期状態が表示状態になります。
:::

:::note info
### 追記
CSSでダイアログ要素に対して`::backdrop`擬似要素を指定すると、
バックドロップのスタイルを変更することができます

:::

## hgroup要素
`h1`〜`h6`までのヘッドライン要素のグループ化に使用されるタグで、
メインタイトルとサブタイトルがある場合などに使用されます。

### 基本的な使い方
```html
<hgroup>
  <h1>メインタイトル</h1>
  <h2>サブタイトル</h2>
</hgroup>
```
ユーザーへの見方は変わりませんが、検索エンジンやスクリーンリーダーが1つの見出しグループとして認識できるようになるものです。

## search
ページないの検索フォームを明示的に明確にできるタグです。
こちらも表示上はかわりませんが、スクリーンリーダーや検索エンジンが認識しやすくなる要素となります。

### 基本的な使い方
```html
<search>
  <form action="/search" method="get">
    <label for="query">検索：</label>
    <input type="search" id="query" name="q" placeholder="キーワードを入力">
    <button type="submit">検索</button>
  </form>
</search>
```

# CSSのあれこれ
次にCSSの便利プロパティや組み合わせなどを紹介します！
また、2024年11月に`Safari18.2`として追加されたプロパティなども紹介します！

## width: fit-content;
`fit-content`は、コンテンツのサイズに応じて**親要素の幅を超えないように**自動調整されるプロパティ値です。

`max-content`との違いは、**親要素を超えるかどうか**で、
`max-content`は親要素は気にせずコンテンツ全体が治る最大の幅をとります。
なので親要素の幅を超える可能性がありますが、`fit-content`は最大が親要素のサイズで調整されます。

## filter
`filter`プロパティは要素にエフェクトをつけるもので、ぼかしや色半円、色相変換などさまざまなエフェクトを付与することができます。

|フィルター名|効果|例|
|---|---|---|
|blur(px)|ぼかしを加える|filter: blur(5px);|
|brightness(%)|明るさを調整|filter: brightness(150%);（明るく）|
|contrast(%)|コントラスト調整|filter: contrast(200%);（強調）|
|grayscale(%)|白黒に変換|filter: grayscale(100%);（完全なモノクロ）|
|invert(%)|色を反転|filter: invert(100%);（ネガポジ反転）|
|opacity(%)|透明度を調整|filter: opacity(50%);（半透明）|
|saturate(%)|色の鮮やかさを調整|filter: saturate(200%);（鮮やか）|
|sepia(%)|セピア調に変換|filter: sepia(100%);（古い写真風）|
|hue-rotate(deg)|色相を変更|filter: hue-rotate(180deg);（青↔オレンジ変換）|
|drop-shadow(x y blur color)|影をつける|filter: drop-shadow(4px 4px 10px black);|

## clip-path
`clip-path`プロパティは要素の表示領域を指定の図形に切り抜くことができるもので、
通常の四角い要素を円形、多角形、カスタムシェイプなどの形にすることができます。

三角形の描画などはこれを使えば一発でできるのでとても便利です...！

|値|効果|例|
|---|---|---|
|circle()|円形に切り抜く|clip-path: circle(50%);|
|ellipse()|楕円形に切り抜く|clip-path: ellipse(50% 30%);|
|polygon()|任意の多角形に切り抜く|clip-path: polygon(0% 0%, 100% 50%, 0% 100%);|
|inset()|四角形の一部をカット|clip-path: inset(10% 20%);|

## テキストの中央寄せと左寄せ
テキスト要素で、画面幅によって行数が変わってしまうことがあります。
その場合に、1行であれば中央寄せ、2行以上の場合は左寄せ、という指定をすることができます。
```css
p {
  inline-size: fit-content;
  margin-inline: auto;
}
```

## テキストの中央寄せの文字量合わせ
画像のように、テキスト量が多い際に、上下の文字の量を同じにすることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3794632/bfd608aa-eeb2-c68e-65e4-35c710b5c472.png)
```css
p {
  text-align: center;
  text-wrap: balance;
}
```

## field-sizing: content;
入力フィールドの大きさを、入力した文字に応じて変化させることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3794632/99e0cb01-eec6-3a4a-3be2-5ee21ce1c85d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3794632/11342edd-3d2a-7127-b940-d65f3849ecaf.png)

:::note
`min-width`と併用して使わないと、3pxくらいの入力フィールドになってしまいます。
:::

## input要素のデフォスタイルの初期化
input要素はデフォのスタイルがたくさんついていて、上書きすることがほとんどだと思います。
3つの指定のみで初期化できるので、覚えておくと便利です。
```css
input {
    appearance:none;
    border: none;
    outline: none;
}

/* textareaの場合は大きさの変更ができないように */
textarea {
    resize: none;
}
```

## scrollbarプロパティ
`Safari 18.2`からスクロールバーの指定が標準プロパティとなりました🎉
それぞれ使用方法と役割を解説していきます！

---

### scrollbar-gutter
スクロールバーがあるとき、ない時で要素の幅を確保するかどうかを指定するプロパティです。

|値|動作|
|---|---|
|`auto`|スクロールバーがある時のみ横幅が確保されます。|
|`stable`|スクロールバーがない時もスクロールバーの横幅が空白となって確保されます。|

---

### scrollbar-width
スクロールバーの表示幅を指定できます。

|値|動作|
|---|---|
|`auto`|通常のスクロールバーが表示されます。|
|`thin`|通常より細いスクロールバーが表示されます。|
|`none`|スクロールバーは表示されなくなりますが、スクロールができるようになります。|

---

### scrollbar-color
スクロールバーの色を指定するプロパティです。
スクロールバー自体の色と、つまみの色をそれぞれ指定することができます。
```css
.element {
    scrollbar-color: red blue; /* 背景がblue、つまみ部分がred */
}
```


## currentColor値
`currentColor`とは、その要素で指定している`color`プロパティの色を参照します。
```css
.element {
    color: green;
    background-color: currentColor; /* greenが適用される */
}
```

また、その要素の`color`プロパティに適用されている値が親要素を継承している場合、
以下のような記述にすることで親要素と同じ色で背景色などを指定することができます。

```css
.parent-element {
    color: green;
}
.parent-element > .element {
    color: currentColor; /* 親要素を継承しているのでgreenが指定される */
    background-color: currentColor; /* .element要素内で指定されているcolorを適用 */
}
```

## mediaルールの簡略化
従来の記述方法では`screen and`を含めた記述が必要なメディアクエリですが、
`Safari 16.4`からは簡略化して記述することが可能になりました。

```css
@media screen and (max-width: 768px) {}

@media (max-width: 768px) {}
```
---

また、`Media Queries Level 4`からは比較演算子を使用して指定範囲を行うことが可能になりました。

```scss
/* 従来の記述例 */
@media (min-width: 320px) and (max-width: 768px) {}

/* 新しい記述例 */
@media (320px <= width <= 768px) {}
```

---

さらに、2年前に全ての主要ブラウザで`コンテナクエリ`が対応されました。

コンテナクエリとは、その要素が親コンテナのサイズに応じてスタイルを変更できるようにしたクエリで、
ビューポートに依存せず親要素のサイズに応じたスタイルの変更が可能になりました！

```scss
.container {
  container-type: inline-size; /* 要素がコンテナとして機能する */
}

@container (min-width: 400px) {
  .element {
    background-color: lightblue;
  }
}

```

## footer最下部固定
body内の主要コンテンツが小さかった場合、footerが画面最下部ではなく画面中央に来てしまうこと、ありますよね...
僕の場合、`main`に対して`100dvh`からヘッダーの高さを引いた大きさを`min-height`として指定していたのですが、もっと簡単でシンプルな方法があったみたいです。

```css
body {
  min-height: 100dvh; /* 最低でも画面幅いっぱいの高さを持つように */
}
footer {
  position: sticky; /* 親要素 (body) のスクロールに応じて位置を固定 */
  top: 100%; /* 親要素 (body) の一番したに配置 */
}
```

# Javascriptのあれこれ
最後にJavascriptの便利記述を紹介します！
知ってる人も多いかもしれませんが、めちゃ便利なので再喝ということで...

## defer属性
```html
<script defer></script>
```
scriptとしてjsを読み込む際に、defer属性をつけることでhtmlのパースに影響なくソースを読み込むことができます。

そのため、実際の表示に関わることが少ないjsはbodyの最下部で読み込むことが多かった`script`も
実際の反映に何ら影響を与えないので`head`タグに記述し読み込ませても大丈夫ということです。
```html
<head>
    <script src="./asetts/js/index.js" defer></script>
</head>
```

## type="module"
scriptとしてjsを読み込む際に、moduleとして読み込むことで
jsファイル内でグローバル定義をしていたとしても他のファイルに干渉することがなくなります。
```js:index_1.js
const text = "abc";
```
```js:index_2.js
const text = "xyz";
```
```html
<script src="./index_1.js" type="module">
<script src="./index_2.js" type="module">

<!-- それぞれのtextの定義は干渉しない -->
```

## 極端に小さな画面でのレスポンシブ
極端に小さなビューポート（横幅）になると、どうしてもCSSのメディアクエリで対応しきるには限界があります。
そのため、対応したい場合はjsで表示倍率を変更することで調節が容易に行えます。

```js
const updateViewport = () => {
  const breakpointWidth = 375; // 375px以下で調整
  const viewportMeta = document.querySelector('meta[name="viewport"]');
  const contentValue =
    window.innerWidth < breakpointWidth
      ? 'width=device-width, initial-scale=0.8' // 小さい画面で倍率調整
      : 'width=device-width, initial-scale=1'; // 通常のビューポート設定
  viewportMeta.setAttribute('content', contentValue);
  console.error(viewportMeta);
};

// リサイズイベントリスナー
window.addEventListener('resize', updateViewport, false);

// 初回読み込み時に調整
window.addEventListener('load', updateViewport);
```