⭐️

【Polaris和訳】Components/Feedback indicators②

2021/11/06に公開

この記事について

この記事は、Polaris/Components/Feedback indicatorsの記事を和訳したものです。

記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。

Shopify アプリのご紹介

Shopify アプリである、「商品ページ発売予告アプリ | リテリア Coming Soon」は、商品ページを買えない状態のまま、発売日時の予告をすることができるアプリです。Shopify で Coming Soon 機能を実現することができます。

https://apps.shopify.com/shopify-application-314?locale=ja&from=daniel

Shopify アプリである、「らくらく日本語フォント設定|リテリア Font Picker」は、ノーコードで日本語フォントを使用できるアプリです。日本語フォントを導入することでブランドを演出することができます。

https://apps.shopify.com/font-picker-1?locale=ja&from=daniel

Spinner

スピナーは、マーチャントのアクションが処理されていることを通知するために使用されます。ローディングステートでは、スピナーはデータチャートのようにスケルトンのローディングコンポーネントで表現できないコンテンツにのみ使用してください。

Web

Default spinner

リクエストされたアクションが処理されていることをマーチャントに通知するために使用します。

Image from Gyazo

React
<Spinner accessibilityLabel="Spinner example" size="large" />
HMTL
<div>
  <span class="Polaris-Spinner Polaris-Spinner--sizeLarge"
    ><svg viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg">
      <path
        d="M15.542 1.487A21.507 21.507 0 00.5 22c0 11.874 9.626 21.5 21.5 21.5 9.847 0 18.364-6.675 20.809-16.072a1.5 1.5 0 00-2.904-.756C37.803 34.755 30.473 40.5 22 40.5 11.783 40.5 3.5 32.217 3.5 22c0-8.137 5.3-15.247 12.942-17.65a1.5 1.5 0 10-.9-2.863z"
      ></path></svg></span
  ><span role="status"><span class="Polaris-VisuallyHidden">Spinner example</span></span>
  <div id="PolarisPortalsContainer"></div>
</div>

Small spinner

デフォルトのスピナーよりも小さいです。

Image from Gyazo

React
<Spinner accessibilityLabel="Small spinner example" size="small" />
HTML
<div>
  <span class="Polaris-Spinner Polaris-Spinner--sizeSmall"
    ><svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
      <path
        d="M7.229 1.173a9.25 9.25 0 1011.655 11.412 1.25 1.25 0 10-2.4-.698 6.75 6.75 0 11-8.506-8.329 1.25 1.25 0 10-.75-2.385z"
      ></path></svg></span
  ><span role="status"><span class="Polaris-VisuallyHidden">Small spinner example</span></span>
  <div id="PolarisPortalsContainer"></div>
</div>

Spinner with focus management

フォーカス状態をコントロールからスピナー、コンテンツに向けるために使用します。

Image from Gyazo

React
function SpinnerWithFocusManagement() {
  const tabs = useRef([
    {
      id: "all-customers",
      content: "All",
      accessibilityLabel: "All customers",
      panelID: "all-customers-content",
    },
    {
      id: "accepts-marketing",
      content: "Accepts marketing",
      panelID: "accepts-marketing-content",
    },
  ])

  const [selected, setSelected] = useState(0)
  const [loading, setLoading] = useState(false)
  const [value, setValue] = useState("")
  const [textFieldFocused, setTextFieldFocused] = useState(false)

  useEffect(() => {
    setTextFieldFocused(!loading)
  }, [loading])

  const handleTabChange = useCallback((selectedTab) => {
    setLoading(true)
    setSelected(selectedTab)
    setTimeout(() => {
      setValue("")
      return setLoading(false)
    }, 1500)
  }, [])

  const handleUrlChange = useCallback((value) => setValue(value), [])

  const handleSubmit = useCallback((_event) => setValue(""), [])

  const label = selected ? "Marketing" : "Customers"
  const sectionMarkup = loading ? (
    <Spinner accessibilityLabel="Loading form field" hasFocusableParent={false} />
  ) : (
    <Form noValidate onSubmit={handleSubmit}>
      <FormLayout>
        <TextField
          value={value}
          focused={textFieldFocused}
          onChange={handleUrlChange}
          label={label}
          autoComplete="off"
        />
        <Button submit>Submit</Button>
      </FormLayout>
    </Form>
  )

  return (
    <Card>
      <Tabs tabs={tabs.current} selected={selected} onSelect={handleTabChange}>
        <Card.Section title={tabs.current[selected].content}>{sectionMarkup}</Card.Section>
      </Tabs>
    </Card>
  )
}
HTML
<div>
  <div class="Polaris-Card">
    <div>
      <div class="Polaris-Tabs__Wrapper">
        <div class="Polaris-Tabs Polaris-Tabs__TabMeasurer">
          <li class="Polaris-Tabs__TabContainer" role="presentation">
            <button
              id="all-customersMeasurer"
              role="tab"
              type="button"
              tabindex="-1"
              class="Polaris-Tabs__Tab Polaris-Tabs__Tab--selected"
              aria-selected="true"
            >
              <span class="Polaris-Tabs__Title">All</span>
            </button>
          </li>
          <li class="Polaris-Tabs__TabContainer" role="presentation">
            <button
              id="accepts-marketingMeasurer"
              role="tab"
              type="button"
              tabindex="-1"
              class="Polaris-Tabs__Tab"
              aria-selected="false"
            >
              <span class="Polaris-Tabs__Title">Accepts marketing</span>
            </button>
          </li>
          <button type="button" class="Polaris-Tabs__DisclosureActivator" aria-label="More tabs">
            <span class="Polaris-Tabs__Title"
              ><span class="Polaris-Icon Polaris-Icon--colorSubdued Polaris-Icon--applyColor"
                ><span class="Polaris-VisuallyHidden"></span
                ><svg
                  viewBox="0 0 20 20"
                  class="Polaris-Icon__Svg"
                  focusable="false"
                  aria-hidden="true"
                >
                  <path
                    d="M6 10a2 2 0 1 1-4.001-.001A2 2 0 0 1 6 10zm6 0a2 2 0 1 1-4.001-.001A2 2 0 0 1 12 10zm6 0a2 2 0 1 1-4.001-.001A2 2 0 0 1 18 10z"
                  ></path></svg></span
            ></span>
          </button>
        </div>
        <ul role="tablist" class="Polaris-Tabs">
          <li class="Polaris-Tabs__TabContainer" role="presentation">
            <button
              id="all-customers"
              role="tab"
              type="button"
              tabindex="0"
              class="Polaris-Tabs__Tab Polaris-Tabs__Tab--selected"
              aria-selected="true"
              aria-controls="all-customers-content"
              aria-label="All customers"
            >
              <span class="Polaris-Tabs__Title">All</span>
            </button>
          </li>
          <li class="Polaris-Tabs__TabContainer" role="presentation">
            <button
              id="accepts-marketing"
              role="tab"
              type="button"
              tabindex="-1"
              class="Polaris-Tabs__Tab"
              aria-selected="false"
              aria-controls="accepts-marketing-content"
            >
              <span class="Polaris-Tabs__Title">Accepts marketing</span>
            </button>
          </li>
          <li class="Polaris-Tabs__DisclosureTab" role="presentation">
            <div>
              <button
                type="button"
                class="Polaris-Tabs__DisclosureActivator"
                aria-label="More tabs"
                tabindex="0"
                aria-controls="Polarispopover2"
                aria-owns="Polarispopover2"
                aria-expanded="false"
              >
                <span class="Polaris-Tabs__Title"
                  ><span class="Polaris-Icon Polaris-Icon--colorSubdued Polaris-Icon--applyColor"
                    ><span class="Polaris-VisuallyHidden"></span
                    ><svg
                      viewBox="0 0 20 20"
                      class="Polaris-Icon__Svg"
                      focusable="false"
                      aria-hidden="true"
                    >
                      <path
                        d="M6 10a2 2 0 1 1-4.001-.001A2 2 0 0 1 6 10zm6 0a2 2 0 1 1-4.001-.001A2 2 0 0 1 12 10zm6 0a2 2 0 1 1-4.001-.001A2 2 0 0 1 18 10z"
                      ></path></svg></span
                ></span>
              </button>
            </div>
          </li>
        </ul>
      </div>
      <div
        class="Polaris-Tabs__Panel"
        id="all-customers-content"
        role="tabpanel"
        aria-labelledby="all-customers"
        tabindex="-1"
      >
        <div class="Polaris-Card__Section">
          <div class="Polaris-Card__SectionHeader">
            <h3 aria-label="All" class="Polaris-Subheading">All</h3>
          </div>
          <form method="post" novalidate="">
            <div class="Polaris-FormLayout">
              <div class="Polaris-FormLayout__Item">
                <div class="">
                  <div class="Polaris-Labelled__LabelWrapper">
                    <div class="Polaris-Label">
                      <label
                        id="PolarisTextField2Label"
                        for="PolarisTextField2"
                        class="Polaris-Label__Text"
                        >Customers</label
                      >
                    </div>
                  </div>
                  <div class="Polaris-Connected">
                    <div class="Polaris-Connected__Item Polaris-Connected__Item--primary">
                      <div class="Polaris-TextField">
                        <input
                          id="PolarisTextField2"
                          autocomplete="off"
                          class="Polaris-TextField__Input"
                          aria-labelledby="PolarisTextField2Label"
                          aria-invalid="false"
                          value=""
                        />
                        <div class="Polaris-TextField__Backdrop"></div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
              <div class="Polaris-FormLayout__Item">
                <button class="Polaris-Button" type="submit">
                  <span class="Polaris-Button__Content"
                    ><span class="Polaris-Button__Text">Submit</span></span
                  >
                </button>
              </div>
            </div>
            <span class="Polaris-VisuallyHidden"
              ><button type="submit" aria-hidden="true" tabindex="-1">Submit</button></span
            >
          </form>
        </div>
      </div>
      <div
        class="Polaris-Tabs__Panel Polaris-Tabs__Panel--hidden"
        id="accepts-marketing-content"
        role="tabpanel"
        aria-labelledby="accepts-marketing"
        tabindex="-1"
      ></div>
    </div>
  </div>
  <div id="PolarisPortalsContainer">
    <div data-portal-id="popover-Polarisportal1"></div>
  </div>
</div>

Props

accessibilityLabel
string
スピナーのアクセシビリティラベル

hasFocusableParent
boolean
フォーカスに基づいて、コンポーネントが正しいアクセシビリティ・ロールを適用できるようにします。

size
"small" | "large"
スピナーのサイズ

アクセシビリティ

SVG は、支援技術に対して矛盾した形で伝えられることがよくあります。スピナーコンポーネントのアクセシビリティは、状況に応じて変化します。親コンポーネントがフォーカス可能な場合、適切なロール属性を適用するために hasFocusableParent prop を設定する必要があります。

最適なユーザーエクスペリエンスのために、accessibilityLabel prop を使用して、支援技術ユーザーにスピナーの目的を知らせます。

Android

デフォルトのスピナー

リクエストされたアクションが処理されていることをマーチャントに通知するために使用します。

Image from Gyazo

フォーカス管理付きスピナー

コントロールからスピナー、コンテンツへとフォーカス状態を誘導するために使用します。

アクセシビリティ

SVG は、支援技術に対して一貫性のない形で伝えられることがよくあります。スピナーコンポーネントのアクセシビリティは、状況に応じて変化します。親コンポーネントがフォーカス可能な場合は、適切なロール属性を適用するために hasFocusableParent のプロップを設定する必要があります。

最適なユーザーエクスペリエンスのために、accessibilityLabel prop を使用して、支援技術ユーザーにスピナーの目的を知らせます。

iOS

デフォルトのスピナー

リクエストされたアクションが処理されていることをマーチャントに通知するために使用します。

Image from Gyazo

フォーカス管理付きスピナー

コントロールからスピナー、コンテンツへとフォーカス状態を誘導するために使用します。

アクセシビリティ

SVG は、支援技術に対して一貫性のない形で伝えられることがよくあります。スピナーコンポーネントのアクセシビリティは、状況に応じて変化します。親コンポーネントがフォーカス可能な場合は、適切なロール属性を適用するために hasFocusableParent のプロップを設定する必要があります。

最適なユーザーエクスペリエンスのために、accessibilityLabel prop を使用して、支援技術ユーザーにスピナーの目的を知らせます。

ベストプラクティス

スピナーコンポーネントは、以下のことを行う必要があります。

  • マーチャントに、リクエストが受信され、まもなくアクションが完了することを通知する。
  • ページロード全体のフィードバックには使用しないでください。
  • 白は、ボタンなどのアクション可能なコンポーネント上の小さなスピナーでのみ使用できます。
  • ウェブ上では、スケルトンローディングと組み合わせて、タイポグラフィではないコンテンツを表現するために使用します。例えば
  • Merchant Analytics のダッシュボードにある折れ線グラフなどです。

コンテンツガイドライン

アクセシビリティラベル

スピナーのアクセシビリティラベルには、以下の点が求められます。

  • 要求されたアクションの状態を正確に説明すること。例えば、「読み込み中」、「送信中」、「処理中」などです。
  • 状態を説明するために、できるだけ少ない単語を使用する。

関連コンポーネント

Toast

Toast コンポーネントは、インターフェイスの下部に表示される邪魔にならないメッセージで、アクションの結果を一目でわかるように素早くフィードバックします。

Web

Basic toast

一般的な確認や重要ではないアクションを伝えるために使用します。例えば、マーチャントが最近行ったアクションが成功したことを知らせるために、トーストメッセージを表示することができます。

Image from Gyazo

React
function ToastExample() {
  const [active, setActive] = useState(false)

  const toggleActive = useCallback(() => setActive((active) => !active), [])

  const toastMarkup = active ? <Toast content="Message sent" onDismiss={toggleActive} /> : null

  return (
    <div style={{ height: "250px" }}>
      <Frame>
        <Page title="Toast example">
          <Button onClick={toggleActive}>Show Toast</Button>
          {toastMarkup}
        </Page>
      </Frame>
    </div>
  )
}
HTML
<div>
  <div style="height: 250px;">
    <div class="Polaris-Frame" data-polaris-layer="true">
      <div class="Polaris-Frame__Skip"><a href="#AppFrameMain">Skip to content</a></div>
      <div class="Polaris-Frame__ContextualSaveBar Polaris-Frame-CSSAnimation--startFade"></div>
      <main class="Polaris-Frame__Main" id="AppFrameMain" data-has-global-ribbon="false">
        <div class="Polaris-Frame__Content">
          <div class="Polaris-Page">
            <div
              class="Polaris-Page-Header Polaris-Page-Header--isSingleRow Polaris-Page-Header--mobileView Polaris-Page-Header--noBreadcrumbs Polaris-Page-Header--mediumTitle"
            >
              <div class="Polaris-Page-Header__Row">
                <div class="Polaris-Page-Header__TitleWrapper">
                  <div>
                    <div class="Polaris-Header-Title__TitleAndSubtitleWrapper">
                      <h1 class="Polaris-Header-Title">Toast example</h1>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <div class="Polaris-Page__Content">
              <button class="Polaris-Button" type="button">
                <span class="Polaris-Button__Content"
                  ><span class="Polaris-Button__Text">Show Toast</span></span
                >
              </button>
            </div>
          </div>
        </div>
      </main>
    </div>
  </div>
  <div id="PolarisPortalsContainer">
    <div data-portal-id="Polarisportal2">
      <div class="Polaris-Frame-ToastManager" aria-live="assertive"></div>
    </div>
  </div>
</div>
<div class="Polaris-Frame-ToastManager" aria-live="assertive"></div>

Multiple toast messages

複数のトーストメッセージを使って、マーチャントに異なるアクションを伝えます。

Image from Gyazo

React
function MultipleToastExample() {
  const [activeOne, setActiveOne] = useState(false)
  const [activeTwo, setActiveTwo] = useState(false)

  const toggleActiveOne = useCallback(() => setActiveOne((activeOne) => !activeOne), [])

  const toggleActiveTwo = useCallback(() => setActiveTwo((activeTwo) => !activeTwo), [])

  const toastMarkup1 = activeOne ? (
    <Toast content="Message sent" onDismiss={toggleActiveOne} />
  ) : null

  const toastMarkup2 = activeTwo ? (
    <Toast content="Image uploaded" onDismiss={toggleActiveTwo} />
  ) : null

  return (
    <div style={{ height: "250px" }}>
      <Frame>
        <Page title="Toast example">
          <ButtonGroup segmented>
            <Button onClick={toggleActiveOne}>Show toast 1</Button>
            <Button onClick={toggleActiveTwo}>Show toast 2</Button>
          </ButtonGroup>
          {toastMarkup1}
          {toastMarkup2}
        </Page>
      </Frame>
    </div>
  )
}
HTML
<div>
  <div style="height: 250px;">
    <div class="Polaris-Frame" data-polaris-layer="true">
      <div class="Polaris-Frame__Skip"><a href="#AppFrameMain">Skip to content</a></div>
      <div class="Polaris-Frame__ContextualSaveBar Polaris-Frame-CSSAnimation--startFade"></div>
      <main class="Polaris-Frame__Main" id="AppFrameMain" data-has-global-ribbon="false">
        <div class="Polaris-Frame__Content">
          <div class="Polaris-Page">
            <div
              class="Polaris-Page-Header Polaris-Page-Header--isSingleRow Polaris-Page-Header--mobileView Polaris-Page-Header--noBreadcrumbs Polaris-Page-Header--mediumTitle"
            >
              <div class="Polaris-Page-Header__Row">
                <div class="Polaris-Page-Header__TitleWrapper">
                  <div>
                    <div class="Polaris-Header-Title__TitleAndSubtitleWrapper">
                      <h1 class="Polaris-Header-Title">Toast example</h1>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <div class="Polaris-Page__Content">
              <div
                class="Polaris-ButtonGroup Polaris-ButtonGroup--segmented"
                data-buttongroup-segmented="true"
              >
                <div class="Polaris-ButtonGroup__Item">
                  <button class="Polaris-Button" type="button">
                    <span class="Polaris-Button__Content"
                      ><span class="Polaris-Button__Text">Show toast 1</span></span
                    >
                  </button>
                </div>
                <div class="Polaris-ButtonGroup__Item">
                  <button class="Polaris-Button" type="button">
                    <span class="Polaris-Button__Content"
                      ><span class="Polaris-Button__Text">Show toast 2</span></span
                    >
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </main>
    </div>
  </div>
  <div id="PolarisPortalsContainer">
    <div data-portal-id="Polarisportal4">
      <div class="Polaris-Frame-ToastManager" aria-live="assertive"></div>
    </div>
  </div>
</div>
<div class="Polaris-Frame-ToastManager" aria-live="assertive"></div>

Toast with custom duration

デフォルトの 5000 ミリ秒を短くしたり長くしたりするときに使います。

Image from Gyazo

React
function ToastWithCustomDurationExample() {
  const [active, setActive] = useState(false)

  const toggleActive = useCallback(() => setActive((active) => !active), [])

  const toastMarkup = active ? (
    <Toast content="Message sent" onDismiss={toggleActive} duration={4500} />
  ) : null

  return (
    <div style={{ height: "250px" }}>
      <Frame>
        <Page title="Toast example">
          <Button onClick={toggleActive}>Show Toast</Button>
          {toastMarkup}
        </Page>
      </Frame>
    </div>
  )
}
HTML
<div>
  <div style="height: 250px;">
    <div class="Polaris-Frame" data-polaris-layer="true">
      <div class="Polaris-Frame__Skip"><a href="#AppFrameMain">Skip to content</a></div>
      <div class="Polaris-Frame__ContextualSaveBar Polaris-Frame-CSSAnimation--startFade"></div>
      <main class="Polaris-Frame__Main" id="AppFrameMain" data-has-global-ribbon="false">
        <div class="Polaris-Frame__Content">
          <div class="Polaris-Page">
            <div
              class="Polaris-Page-Header Polaris-Page-Header--isSingleRow Polaris-Page-Header--mobileView Polaris-Page-Header--noBreadcrumbs Polaris-Page-Header--mediumTitle"
            >
              <div class="Polaris-Page-Header__Row">
                <div class="Polaris-Page-Header__TitleWrapper">
                  <div>
                    <div class="Polaris-Header-Title__TitleAndSubtitleWrapper">
                      <h1 class="Polaris-Header-Title">Toast example</h1>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <div class="Polaris-Page__Content">
              <button class="Polaris-Button" type="button">
                <span class="Polaris-Button__Content"
                  ><span class="Polaris-Button__Text">Show Toast</span></span
                >
              </button>
            </div>
          </div>
        </div>
      </main>
    </div>
  </div>
  <div id="PolarisPortalsContainer">
    <div data-portal-id="Polarisportal6">
      <div class="Polaris-Frame-ToastManager" aria-live="assertive"></div>
    </div>
  </div>
</div>
<div class="Polaris-Frame-ToastManager" aria-live="assertive"></div>

Toast with action

マーチャントがメッセージに基づいて行動する能力がある場合に使用します。例えば、変更の取り消しやアクションの再試行などです。

Image from Gyazo

React
function ToastWithActionExample() {
  const [active, setActive] = useState(false)

  const toggleActive = useCallback(() => setActive((active) => !active), [])

  const toastMarkup = active ? (
    <Toast
      content="Image deleted"
      action={{
        content: "Undo",
        onAction: () => {},
      }}
      duration={10000}
      onDismiss={toggleActive}
    />
  ) : null

  return (
    <div style={{ height: "250px" }}>
      <Frame>
        <Page title="Toast example">
          <Button onClick={toggleActive}>Show Toast</Button>
          {toastMarkup}
        </Page>
      </Frame>
    </div>
  )
}
HTML
<div>
  <div style="height: 250px;">
    <div class="Polaris-Frame" data-polaris-layer="true">
      <div class="Polaris-Frame__Skip"><a href="#AppFrameMain">Skip to content</a></div>
      <div class="Polaris-Frame__ContextualSaveBar Polaris-Frame-CSSAnimation--startFade"></div>
      <main class="Polaris-Frame__Main" id="AppFrameMain" data-has-global-ribbon="false">
        <div class="Polaris-Frame__Content">
          <div class="Polaris-Page">
            <div
              class="Polaris-Page-Header Polaris-Page-Header--isSingleRow Polaris-Page-Header--mobileView Polaris-Page-Header--noBreadcrumbs Polaris-Page-Header--mediumTitle"
            >
              <div class="Polaris-Page-Header__Row">
                <div class="Polaris-Page-Header__TitleWrapper">
                  <div>
                    <div class="Polaris-Header-Title__TitleAndSubtitleWrapper">
                      <h1 class="Polaris-Header-Title">Toast example</h1>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <div class="Polaris-Page__Content">
              <button class="Polaris-Button" type="button">
                <span class="Polaris-Button__Content"
                  ><span class="Polaris-Button__Text">Show Toast</span></span
                >
              </button>
            </div>
          </div>
        </div>
      </main>
    </div>
  </div>
  <div id="PolarisPortalsContainer">
    <div data-portal-id="Polarisportal8">
      <div class="Polaris-Frame-ToastManager" aria-live="assertive"></div>
    </div>
  </div>
</div>
<div class="Polaris-Frame-ToastManager" aria-live="assertive"></div>

Error toast

エラートーストはまだ利用可能で、システム内で使用されていますが、その使用はお勧めできません。接続の問題など、マーチャントが原因ではないエラーの場合に使用してください。エラートーストは、何が悪かったのかをわかりやすく伝える必要があり、3 語を超えてはいけません。その他の種類のエラーメッセージについては、エラーメッセージのガイドラインに従ってください。

Image from Gyazo

React
function ErrorToastExample() {
  const [active, setActive] = useState(false)

  const toggleActive = useCallback(() => setActive((active) => !active), [])

  const toastMarkup = active ? (
    <Toast content="Server error" error onDismiss={toggleActive} />
  ) : null

  return (
    <div style={{ height: "250px" }}>
      <Frame>
        <Page title="Toast example">
          <Button onClick={toggleActive}>Show Toast</Button>
          {toastMarkup}
        </Page>
      </Frame>
    </div>
  )
}
HTML
<div>
  <div style="height: 250px;">
    <div class="Polaris-Frame" data-polaris-layer="true">
      <div class="Polaris-Frame__Skip"><a href="#AppFrameMain">Skip to content</a></div>
      <div class="Polaris-Frame__ContextualSaveBar Polaris-Frame-CSSAnimation--startFade"></div>
      <main class="Polaris-Frame__Main" id="AppFrameMain" data-has-global-ribbon="false">
        <div class="Polaris-Frame__Content">
          <div class="Polaris-Page">
            <div
              class="Polaris-Page-Header Polaris-Page-Header--isSingleRow Polaris-Page-Header--mobileView Polaris-Page-Header--noBreadcrumbs Polaris-Page-Header--mediumTitle"
            >
              <div class="Polaris-Page-Header__Row">
                <div class="Polaris-Page-Header__TitleWrapper">
                  <div>
                    <div class="Polaris-Header-Title__TitleAndSubtitleWrapper">
                      <h1 class="Polaris-Header-Title">Toast example</h1>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <div class="Polaris-Page__Content">
              <button class="Polaris-Button" type="button">
                <span class="Polaris-Button__Content"
                  ><span class="Polaris-Button__Text">Show Toast</span></span
                >
              </button>
            </div>
          </div>
        </div>
      </main>
    </div>
  </div>
  <div id="PolarisPortalsContainer">
    <div data-portal-id="Polarisportal10">
      <div class="Polaris-Frame-ToastManager" aria-live="assertive"></div>
    </div>
  </div>
</div>
<div class="Polaris-Frame-ToastManager" aria-live="assertive"></div>

Props

action

Object

accessibilityLabel
型: string
スクリーン・リーダーのための視覚的に隠されたテキスト


content
型: string
アクションが表示するコンテンツ


external
型: boolean
url を新しいタブで開くようにする


id
型: string
アクションのユニークな識別子です。


url
型: string
アクションの中でレンダリングされる、リンク先です。


onAction
型: () => void
アクションが発生したときのコールバック


onMouseEnter
型: () => void
マウスが入力されたときのコールバック


onTouchStart
型: () => void
要素がタッチされたときのコールバック

Action
メッセージの横にアクションを追加します。


contentRequired
string
トーストメッセージに表示されるコンテンツです。


duration
number
トーストメッセージが持続する時間の長さをミリ秒単位で指定します。


error
boolean
エラーのトーストを表示します。


onDismiss Required
() => void
解散アイコンがクリックされたときのコールバック

アクセシビリティ

トーストコンポーネントのコンテンツは、aria-live="polite "を使用した ARIA ライブリージョンとして実装されています。トーストが表示された場合、スクリーンリーダーは他の緊急性の高いアナウンスの後にトーストのテキストをアナウンスする必要があります。

マーチャントがすぐに行動しなければならない重要な情報にトーストを使用することは避けてください。トーストは、弱視や手先の器用さに欠けるマーチャントがアクセスしにくい場合があります。

  • 自動的に表示されない
  • キーボードで簡単にアクセスできない
  • マーチャントの現在のフォーカスの近くに表示されない場合がある。

アクション付きトースト

マーチャントによってはトーストアクションにアクセスするのが難しい場合があるので、マーチャントが他の方法でトースト内のアクションを実行できるかどうかを確認してください。トーストアクションがページ上の他の場所で利用できない場合、例えばセクションを再読み込みするリトライアクションのように、ブラウザのリフレッシュなどの予備のアクションを持つべきです。

アクション付きのトーストは、マーチャントがアクションを起こすのに十分な時間を与えるために、少なくとも 10,000 ミリ秒は持続させるべきです。

Android

デフォルトのトースト

デフォルトのトーストは、有益で中立的なフィードバックに使用します。

Image from Gyazo

成功トースト

何かが成功したことを示すために、成功トーストを使用します。例えば、製品のアップデートが成功した場合などです。

Image from Gyazo

エラートースト

エラートーストはシステム上ではまだ利用可能であり、使用されていますが、その使用は推奨されません。接続の問題など、マーチャントが原因ではないエラーに使用してください。エラートーストは、何が悪かったのかをわかりやすく伝える必要があり、3 語を超えてはいけません。その他のエラーメッセージについては、エラーメッセージのガイドラインに従ってください。

Image from Gyazo

アクション付き

マーチャントがメッセージに基づいて行動できる場合は、アクションを使用します。例えば、変更の取り消しやアクションの再試行などです。アクションラベルは短く、1 つの動詞アクションが望ましいです。

Image from Gyazo

iOS

デフォルトのトースト

情報提供や中立的なフィードバックには、デフォルトのトーストを使用します。

iOS では、メッセージを再確認したい場合には、アイコンを利用できます。

Image from Gyazo

成功トースト

成功トーストは、何かが成功したことを示すために使用します。例えば、製品のアップデートが成功した場合などです。

iOS では、メッセージを再確認したい場合に、アイコンを使用することができます。

Image from Gyazo

エラートースト

エラートーストはシステム上ではまだ利用可能であり、使用されていますが、その使用は推奨されません。接続の問題など、マーチャントが原因ではないエラーの場合に使用してください。エラートーストは、何が悪かったのかをわかりやすく伝える必要があり、3 語を超えてはいけません。その他のエラーメッセージについては、エラーメッセージのガイドラインに従ってください。

iOS では、メッセージを強調したい場合に、アイコンを使用することができます。

Image from Gyazo

アクションの場合

マーチャントがメッセージに基づいて行動することができる場合には、アクションを使用します。例えば、変更を元に戻す、アクションを再試行するなどです。アクションラベルは短く、1 つの動詞のアクションが望ましいです。

Image from Gyazo

必要なコンポーネント

トーストコンポーネントは、フレームコンポーネントで包まれている必要があります。

ベストプラクティス

トーストは

  • アクションを確認するための短いメッセージに使用する
  • 3 語以上は使わない。
  • エラーメッセージに使用することは稀です。

どんなときに使うか:

  • 成功メッセージに使う
  • 重要ではないエラーで、その場に関連性があり、3 語で説明できる場合にのみ使用します。例えば、インターネットの接続に問題がある場合、「インターネットが切断されました」と表示します。

使ってはいけないとき:

  • エラーメッセージにトーストを使うのは避けましょう。持続的なエラーについては、常にバナーを使用してマーチャントに目立つように通知するようにしてください。

コンテンツのガイドライン

メッセージ
トーストメッセージは

  • 短く、肯定的であること
  • 名詞+動詞のパターンで書かれていること

Image from Gyazo

アクション付きトースト

トーストにアクションを含めるのは、同じアクションがページの他の場所でも利用できる場合のみです。例えば

  • マーチャントがセクションを再読み込みする必要がある場合、トーストの中でコールトゥアクション[Reload]を提供します。トーストメッセージを見逃した場合は、ページ全体を更新することもできます。
  • マーチャントが画像を削除した場合、削除を[元に戻す]というオプションを提供します。トーストメッセージを見落としても、他の場所から画像を取り出すことができます。

アクションが必要です。

  • アクションラベルは短く、できれば 1 動詞にしましょう。
  • キャンセル]のような、トーストを消すためのアクションを持たない。解散するための[X]は、すでにコンポーネントに含まれています。
  • アクセシビリティを考慮して、最低でも 10,000 ミリ秒の継続時間で使用すること。

Image from Gyazo

関連コンポーネント

Shopify アプリのご紹介

Shopify アプリである、「商品ページ発売予告アプリ | リテリア Coming Soon」は、商品ページを買えない状態のまま、発売日時の予告をすることができるアプリです。Shopify で Coming Soon 機能を実現することができます。

https://apps.shopify.com/shopify-application-314?locale=ja&from=daniel

Shopify アプリである、「らくらく日本語フォント設定|リテリア Font Picker」は、ノーコードで日本語フォントを使用できるアプリです。日本語フォントを導入することでブランドを演出することができます。

https://apps.shopify.com/font-picker-1?locale=ja&from=daniel

Discussion

ログインするとコメントできます