1 of 26

ButtonとLink、どう実装する?

2023/08/30 Encraft #6 @yoshiko_pg

2 of 26

About me

よしこ @yoshiko_pg

株式会社ナレッジワークのフロントエンドエンジニア

GUIをSPAとして作るのが好き。

自社での技術スタックや設計をZennで公開しています。

https://zenn.dev/yoshiko

© Knowledge Work Inc.

3 of 26

要件

4 of 26

要件

  •      + onClick によるアクション
  •      + href によるページ遷移
  •      + onClick によるアクション
  •      + href によるページ遷移

4

4

デザインデータから導き出された必要そうな要件はこんな感じ

※ 便宜上クリック/onClickと記しますがキーボードその他の操作も対象としています

© Knowledge Work Inc.

5 of 26

要件整理

5

見た目 \ 挙動

onClick

href

ボタン

リンク

© Knowledge Work Inc.

6 of 26

要件整理

6

見た目 \ 挙動

onClick

href

ボタン

リンク

ちょっとまって

© Knowledge Work Inc.

7 of 26

要件整理

これ本当に要るの?

7

見た目 \ 挙動

onClick

href

ボタン

リンク

© Knowledge Work Inc.

8 of 26

要件整理

これ本当に要るの?

( って思った人手あげて〜 )

8

見た目 \ 挙動

onClick

href

ボタン

リンク

© Knowledge Work Inc.

9 of 26

当初は存在したが、途中から

アクションにリンクの見た目を

使うのをやめる整理をした。

今は全て Transparent Button に

置き換わっている。

→「リンクみたいなボタン」は不要

9

ナレッジワーク

プロダクトガイドラインより抜粋 ▶

© Knowledge Work Inc.

10 of 26

ページ遷移がメインアクションになる文脈が存在する。

たとえば「ユーザー追加」でユーザー追加ページに

遷移する要件の場合、必要な振る舞いはリンクである。

だが「ユーザー追加」の見た目がリンクだと、

それが現在期待するメインのアクションであっても、

そのことが直感的にユーザーに伝わらなくなる。

→「ボタンみたいなリンク」は必要な場面がある

10

© Knowledge Work Inc.

11 of 26

要件おさらい

11

見た目 \ 挙動

onClick

href

ボタン

リンク

© Knowledge Work Inc.

12 of 26

要件おさらい

12

見た目 \ 挙動

onClick

href

ボタン

リンク

まだある

© Knowledge Work Inc.

13 of 26

要件おさらい

13

見た目 \ 挙動

onClick

href

ボタン

リンク

任意の内容

リソースに応じたカスタム選択UI

リソースに応じた詳細への遷移UI

© Knowledge Work Inc.

14 of 26

要件おさらい

これらをうまく、いい感じに実装したい

14

見た目 \ 挙動

onClick

href

ボタン

リンク

任意の内容

リソースに応じたカスタム選択UI

リソースに応じた詳細への遷移UI

© Knowledge Work Inc.

15 of 26

実装

16 of 26

考え方

まずは見た目と振る舞いを分離して考える

16

16

© Knowledge Work Inc.

17 of 26

まずは見た目と振る舞いを分離して考える

17

リンク

クリック

アクション

振る舞い

見た目

任意の

なにか

© Knowledge Work Inc.

18 of 26

まずは見た目と振る舞いを分離して考える

18

リンク

クリック

アクション

振る舞い

見た目

任意の

なにか

© Knowledge Work Inc.

19 of 26

まずは見た目と振る舞いを分離して考える

19

リンク

クリック

アクション

振る舞い

見た目

任意の

なにか

<Clickable

onClick={}

<Linkable

href=””

<Linkable href=””>

任意のなにか

</Linkable>

<Clickable onClick={}>

任意のなにか

</Clickable>

<Linkable href=””>

<Button>ボタン</Button>

</Linkable>

<Clickable onClick={}>

<Button>ボタン</Button>

</Clickable>

<Linkable href=””>

<Link>リンク</Link>

</Linkable>

<Button />

<Link />

© Knowledge Work Inc.

20 of 26

使いやすいように調整

20

リンク

クリック

アクション

振る舞い

見た目

任意の

なにか

<Clickable

onClick={}

<Linkable

href=””

<Linkable href=””>

任意のなにか

</Linkable>

<Clickable onClick={}>

任意のなにか

</Clickable>

<Button href=””>

ボタン

</Button>

<Button onClick={}>

ボタン

</Button>

<Link href=””>

リンク

</Link>

<Button />

<Link />

必ず囲うものは中に取り込み

使いやすく簡略化

© Knowledge Work Inc.

21 of 26

振る舞い - <Clickable />

見た目を持たず、ボタンの振る舞いだけを持つComponent

  • 必須Props: onClick , children
  • 任意Props: button要素の持つProps + div要素の持つProps
  • 実体: div
    • HTMLのInteractive Content (button) を使いたいが、buttonの下に許可されているのはPhrasing Contentのみなのでdivなどを入れられない。Componentとして任意のchildrenを持たせる際にその制約は大変なので、要素はdivにしてボタンの振る舞いは自前で実装している
    • roleをつける、tabIndexをつける、keyboardによる操作の対応、など

ほんとはbutton使えたら一番楽なんですけどね。

中身のマークアップが複雑かつクリッカブルな要素、みんなどうしてますか?

21

21

© Knowledge Work Inc.

22 of 26

振る舞い - <Linkable />

見た目を持たず、リンクの振る舞いだけを持つComponent

  • 必須Props: href , children
  • 任意Props: a要素の持つProps + disabled , replace
  • 実体: a
    • Propsに応じてnext/linkのLinkとHTMLのa要素を使い分けている
    • リンクがここに集約されるので、next/linkのインターフェイスが変わったときの対応も楽だった
    • referrerPolicy 属性や rel 属性などの漏れがちな指定も集約できる
    • Clickableのようにdisabled属性が欲しいことがたびたびあったので独自に追加

22

22

© Knowledge Work Inc.

23 of 26

見た目+振る舞い - <Button />

ボタンの見た目と、ボタンもしくはリンクの振る舞いを持つComponent

  • 必ずインタラクティブになるので、ClickableとLinkableの振る舞いは内部に取り込んだ
    • href が渡されたら内部的にLinkableを使い、そうでなければHTMLのbutton要素を使う

(ButtonではHTMLのbutton要素を使うのが適切と思ったのでClickableは使わないことにした)

  • button要素の場合、typeのデフォルトはsubmitではなくbutton

  • 見た目のバリエーション
    • カラーバリエーション、サイズバリエーションを持つ
    • 任意のアイコンを入れられる
    • loading属性でloadingを表示できる

23

23

© Knowledge Work Inc.

24 of 26

見た目+振る舞い - <Link />

リンクの見た目と振る舞いを持つComponent

  • 必ずリンクになるので、Linkableの振る舞いは内部に取り込んだ
    • 内部的には直接 Linkable を返している
    • このComponentがやっていることはリンクの見た目をCSSで実装し、LinkableのclassNameに渡すこと

24

24

© Knowledge Work Inc.

25 of 26

まとめ

振る舞いと見た目を分離して実装し、振る舞いだけでも利用できるようにすることで、

複雑なインタラクションの実装を一箇所に集約しています。

カスタムコンポーネントを実装中にクリックアクション / リンクの振る舞いが必要になったときにも、

<Clickable> <Linkable> で囲うだけで配慮込みの振る舞いを実現できるので、効率的に機能実装ができます。

うちはこうしているよという紹介なので、うちは他のやりかたでうまくやれているよという話があれば知りたいです!

25

© Knowledge Work Inc.

26 of 26