toast コンポーネントの作成

アダプティブでアクセス可能なトースト コンポーネントを作成する方法の基本概要。

この投稿では、トースト コンポーネントを作成する方法について考えを共有したいと思います。ぜひ、 demo

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
</ph> デモ

動画で視聴したい場合は、この投稿の YouTube バージョンをご利用ください。

概要

トーストは、ユーザー向けの非インタラクティブかつ受動的な非同期の短いメッセージです。 一般的に、ユーザーに通知するためのインターフェースのフィードバック パターンとして使用されます。 アクションの結果に関するものです

インタラクション数

トーストは通知とは異なり アラートプロンプトを作成します。 インタラクティブではありません破棄したり存続させたりする意図はありません。 通知は、より重要な情報に関するものです。 (ページレベルではなく)インタラクション、またはシステムレベルのメッセージが必要な場合。 トーストは、他の通知戦略よりも受動的です。

マークアップ

<output> 要素はトーストに適しています。 できます。正しい HTML があれば、JavaScript と 多数の JavaScript が存在します。

トースト

<output class="gui-toast">Item added to cart</output>

より 包括的 role="status" を追加します。これにより ブラウザが <output> 要素に暗黙的な指定を行わなかった場合は、暗黙的に ロール 使用できます。

<output role="status" class="gui-toast">Item added to cart</output>

トースト コンテナ

一度に複数のトーストを表示できます。複数のアプリケーションを コンテナが使用されます。このコンテナは、画像のサイズも トーストが画面に表示されます。

<section class="gui-toast-group">
  <output role="status">Wizard Rose added to cart</output>
  <output role="status">Self Watering Pot added to cart</output>
</section>

レイアウト

トーストを inset-block-end トーストが追加されると、その画面の端から積み重ねられます。

GUI コンテナ

トースト コンテナは、トーストを表示するためのレイアウト作業をすべて行います。です。 fixed でビューポートに移動し、論理プロパティを使用します。 inset: さらに、同じ block-end の端からわずかに padding が加わりました。

.gui-toast-group {
  position: fixed;
  z-index: 1;
  inset-block-end: 0;
  inset-inline: 0;
  padding-block-end: 5vh;
}

DevTools のボックスサイズとパディングが .gui-toast-container 要素にオーバーレイされているスクリーンショット

トースト コンテナは、ビューポート内に配置するだけでなく、 トーストを配置して分配できるグリッドコンテナです。アイテムは、 justify-content でグループ化し、個別に justify-items で中央に配置しています。 トーストが触れないように gap を少し入れてください。

.gui-toast-group {
  display: grid;
  justify-items: center;
  justify-content: center;
  gap: 1vh;
}

トースト グループに CSS グリッド オーバーレイが表示されているスクリーンショット
トーストの子要素間のスペースとギャップがハイライト表示されます。

GUI トースト

個々のトーストには padding があり、角が柔らかく、 border-radius, と min() 関数を使用して、 モバイルとパソコンでのサイズ調整に役立ちます次の CSS のレスポンシブ サイズ トーストがビューポートの 90% を超えて拡大するのを防ぎます。 25ch

.gui-toast {
  max-inline-size: min(25ch, 90vw);
  padding-block: .5ch;
  padding-inline: 1ch;
  border-radius: 3px;
  font-size: 1rem;
}

パディングと枠線がある単一の .gui-toast 要素のスクリーンショット
表示されます。

スタイル

レイアウトと配置を設定したら、ユーザーへの適応をサポートする CSS を追加する 設定や操作を行えます

トースト コンテナ

トーストはインタラクティブではありません。タップやスワイプでは何も起こりませんが、 現在、ポインタ イベントを消費しています。トーストによるデータの窃取を防ぐ 次の CSS でクリックするだけです

.gui-toast-group {
  pointer-events: none;
}

GUI トースト

カスタム プロパティ、HSL、カスタム プロパティを使用して、トーストにライトまたはダークのアダプティブ テーマを設定します。 優先メディアクエリです。

.gui-toast {
  --_bg-lightness: 90%;

  color: black;
  background: hsl(0 0% var(--_bg-lightness) / 90%);
}

@media (prefers-color-scheme: dark) {
  .gui-toast {
    color: white;
    --_bg-lightness: 20%;
  }
}

アニメーション

新しいトーストが画面に入ると、アニメーションとともに表示されます。 モーションの低減に対応するには、次の方法で translate 値を 0 に設定します。 ただし、モーション設定メディアでモーションの値を長さに更新する queryすべてのユーザーにアニメーションが表示されますが、トーストの移動が表示されるのは一部のユーザーに限られます 移動します

トースト アニメーションで使用されるキーフレームは次のとおりです。CSS が 入口、待ち時間、トーストの出口を 1 つのアニメーションでまとめて表示します。

@keyframes fade-in {
  from { opacity: 0 }
}

@keyframes fade-out {
  to { opacity: 0 }
}

@keyframes slide-in {
  from { transform: translateY(var(--_travel-distance, 10px)) }
}

次に、トースト要素が変数を設定し、キーフレームをオーケストレートします。

.gui-toast {
  --_duration: 3s;
  --_travel-distance: 0;

  will-change: transform;
  animation: 
    fade-in .3s ease,
    slide-in .3s ease,
    fade-out .3s ease var(--_duration);
}

@media (prefers-reduced-motion: no-preference) {
  .gui-toast {
    --_travel-distance: 5vh;
  }
}

JavaScript

スタイルとスクリーン リーダーのアクセス可能な HTML を準備したら、 ユーザーに応じたトーストの作成、追加、破棄をオーケストレートする できます。トースト コンポーネントのデベロッパー エクスペリエンスは最小限にし、 簡単に使い始めることができます。

import Toast from './toast.js'

Toast('My first toast')

トースト グループとトーストの作成

トースト モジュールが JavaScript から読み込まれるときに、トースト コンテナを作成する必要があります ページに追加しますbody より前に要素を追加したので、次のようになります。 コンテナが対象のコンテナより上にあるため、z-index のスタックに関する問題が生じる可能性は低くなります。 すべての本文の要素が含まれます

const init = () => {
  const node = document.createElement('section')
  node.classList.add('gui-toast-group')

  document.firstElementChild.insertBefore(node, document.body)
  return node
}

head タグと body タグの間のトースト グループのスクリーンショット。

init() 関数がモジュールに対して内部で呼び出され、要素がスタッシュされる Toaster として:

const Toaster = init()

トースト HTML 要素を作成するには、createToast() 関数を使用します。「 トースト用のテキストが必要で、<output> 要素が作成され、装飾されます。 それをクラスと属性とともに指定し、テキストを設定してノードを返します。

const createToast = text => {
  const node = document.createElement('output')
  
  node.innerText = text
  node.classList.add('gui-toast')
  node.setAttribute('role', 'status')

  return node
}

1 つまたは複数のトーストを管理する

JavaScript により、トーストを格納するためのコンテナがドキュメントに追加され、 トーストを追加できますaddToast() 関数は、1 つのリクエストに対する トーストを多数開発できますまずトーストの数と モーションに問題がないか確認します この情報を使用してトーストを追加するか 他のトーストが「スペースを作りたい」ように見えるアニメーションにする新しいトーストを作成します

const addToast = toast => {
  const { matches:motionOK } = window.matchMedia(
    '(prefers-reduced-motion: no-preference)'
  )

  Toaster.children.length && motionOK
    ? flipToast(toast)
    : Toaster.appendChild(toast)
}

最初のトーストを追加するとき、Toaster.appendChild(toast) はトーストを追加します。 CSS アニメーションをトリガーするページ: アニメーションイン、待機 3s、アニメーションアウト。 flipToast() は、次の手法を使用して、既存のトーストがある場合に呼び出されます。 「FLIP」作成者: Lewis。考え方としては コンテナの位置(新しいトーストを追加する前と後)に配置します。 トースターが現在どこにあるか、これからどこに行くかを示すものと考えてください。 移動することもできます

const flipToast = toast => {
  // FIRST
  const first = Toaster.offsetHeight

  // add new child to change container size
  Toaster.appendChild(toast)

  // LAST
  const last = Toaster.offsetHeight

  // INVERT
  const invert = last - first

  // PLAY
  const animation = Toaster.animate([
    { transform: `translateY(${invert}px)` },
    { transform: 'translateY(0)' }
  ], {
    duration: 150,
    easing: 'ease-out',
  })
}

CSS グリッドがレイアウトのリフトを行います。新しいトーストが追加されると スペースを入れておきます。一方、ウェブ アニメーション 元の位置からコンテナをアニメーション化します。

すべての JavaScript のまとめ

Toast('my first toast') が呼び出されると、トーストが作成され、ページに追加されます (新しいトーストに合わせてコンテナがアニメーション化されている可能性もあります)。 Promise 返され、作成されたトーストが 視聴: Promise の解決のための CSS アニメーション完了(3 つのキーフレーム アニメーション)。

const Toast = text => {
  let toast = createToast(text)
  addToast(toast)

  return new Promise(async (resolve, reject) => {
    await Promise.allSettled(
      toast.getAnimations().map(animation => 
        animation.finished
      )
    )
    Toaster.removeChild(toast)
    resolve() 
  })
}

このコードでわかりにくい部分は Promise.allSettled() 関数にあるように感じた と toast.getAnimations() のマッピングを行います。今回は複数のキーフレーム アニメーションを使った トーストのすべての処理が終了したことを確信するには、 リクエストの各メソッドと、 finished 完了がモニタリングされます。 allSettled すべての Promise が処理できたら完了として自身を解決します。 満たされていることを示します。await Promise.allSettled() を使用すると、次のようになります。 コードを使用して確実に要素を削除し、トーストが処理を完了したことを 説明します。最後に、resolve() を呼び出すことで高レベルの Toast Promise が満たされるため、 トーストが表示された後、デベロッパーはクリーンアップやその他の作業を行うことができます。

export default Toast

最後に、Toast 関数がモジュールからエクスポートされ、他のスクリプトが インポートして使用します。

トースト コンポーネントの使用

トースト、つまりトーストのデベロッパー エクスペリエンスを使用するには、 Toast 関数を呼び出して、メッセージ文字列で呼び出します。

import Toast from './toast.js'

Toast('Wizard Rose added to cart')

開発者がクリーンアップ処理などを行う場合は 非同期と RNN のどちらを使用しても 待機

import Toast from './toast.js'

async function example() {
  await Toast('Wizard Rose added to cart')
  console.log('toast finished')
}

まとめ

どのようにやり方をしたかわかったので、どのように感じますか? ‽ 🙂?

アプローチを多様化して、ウェブで構築するすべての方法を学びましょう。 デモを作成し、ツイートしてリンクを送ってください 下の [コミュニティリミックス]セクションに アクセスしてください

コミュニティ リミックス