www.wantedly.com
Open in
urlscan Pro
13.230.249.96
Public Scan
URL:
https://www.wantedly.com/companies/wantedly/post_articles/505248
Submission: On October 18 via manual from JP — Scanned from JP
Submission: On October 18 via manual from JP — Scanned from JP
Form analysis
1 forms found in the DOM<form class="FeedbackForm__Form-sc-k1iym7-1 bmwejc">
<div class="FeedbackForm__TextAreaWrapper-sc-k1iym7-2 ezZEEz">
<div cursor="text" display="block" class="useTouchArea__TouchArea-sc-101jzj6-0 eSyIPr"><textarea shape="R4" name="message" placeholder="Wantedlyへのご意見・ご要望お待ちしています!"
class="TextArea__StyledTextArea-sc-dlorok-0 fPkJSq FeedbackForm__StyledTextArea-sc-k1iym7-3 bbUQzo" style="cursor: text; border: none;"></textarea></div>
</div>
<div class="FeedbackForm__Actions-sc-k1iym7-4 dIUSdW">
<div class="useTouchArea__TouchArea-sc-101jzj6-0 bQjBvr">
<a height="36" font-size="14" elevation="0" shape="R100" href="https://help.wantedly.com/hc/ja/requests/new" rel="noreferrer noopener" target="_blank" class="Button__StyledButton-sc-627uvk-0 Button__StyledSolidButton-sc-627uvk-1 ehOumz gjumsv" style="padding: 0px 20px;">返信が必要な方はこちら</a>
</div>
<div disabled="" class="useTouchArea__TouchArea-sc-101jzj6-0 iNazaq"><button height="36" font-size="14" elevation="0" shape="R100" disabled="" type="submit"
class="Button__StyledButton-sc-627uvk-0 Button__StyledSolidButton-sc-627uvk-1 qXMkE gNzmdC FeedbackForm__SubmitButton-sc-k1iym7-5 ilXAUu">Wantedlyへの意見を送る</button></div>
</div>
</form>
Text Content
1 / 5 募集 ストーリー はたらくを面白くする、国内最大のシゴトのSNS 採用担当者の方はこちら 無料でログイン Wantedly Engineer Blog Wantedly, Inc. フォローhttps://wantedlyinc.com 東京都 フォロー ホーム私たちについてメンバーストーリー募集 HTML+CSSで作る汎用タッチエリア 原 将己 Infrastructure Squad / インフラ・バックエンドエンジニア on 2023/05/10 Photo by charlesdeluvio on Unsplash タッチエリアとは タッチエリア (touch target) とは、UIコンポーネントがクリックやタップなどの座標指定の操作に反応する範囲のことです。操作性をよくするために、タッチエリアは意図的に見た目上の大きさよりも大きく確保されることがあります。 > Touch targets are the parts of the screen that respond to user input. They > extend beyond the visual bounds of an element. https://m2.material.io/develop/web/supporting/touch-target WANTEDLYにおけるタッチエリア Wantedlyでは独自のUIデザインシステムを運用しています。これについては以前以下のような記事で触れています: ノンデザイナーズ・Wantedly デザインシステム完全理解ペーパー | Wantedly Engineer Blog Wantedly では新卒含む新入社員向けに研修を毎年実施しています。これは「新入社員向け」といいつつ既存の社員も自由に参加できるものです。今年はこの研修のフォーマットを借りて、Wantedly のプロダクト開発を支える重要な概念のひとつである「Wantedly の UI デザインシステム」についての研修を、ソフトウェアエンジニアの @izumin5210 (筆者) とプロダクトデザイナーの @NishaMe で実施しました。 デザインの構造を正しく捉えることは、UI の実装を専門にしているかどうかを問わ https://www.wantedly.com/companies/wantedly/post_articles/395772 UIデザインシステムでは、一貫したUIを作るためにまずいくつかの原則を定義し、その原則にしたがって共通コンポーネントを定義しています。実際に提供される画面は、なるべくこの原則と共通コンポーネントにしたがって作られます。 Web frontendをはじめとした各フロントエンド技術はこの構成をなぞり、原則と共通コンポーネントがコード上で表現されるように作られています。 そして、Wantedlyではそのような共通コンポーネントのひとつとして "TouchArea" のようなものがあると考えています。 WantedlyのUIデザインシステムでは、TouchAreaコンポーネントの「レイアウト上の境界」と「タッチエリア」は一致するものとして定義されています。つまり、タッチエリアは各TouchAreaの専有する領域であり、他の要素の侵入は許さないようになっています。TouchAreaを並べると、タッチエリアは隙間や重なりなく敷き詰められることになります。 デザインシステムを実装するには、この「汎用タッチエリア」をうまくコードで表現する必要があります。 内側か、外側か TouchAreaの基本的なインターフェースは次のようなものであることが期待されます。 // buttonのタッチエリアを拡大する <TouchArea> {/* 実際にはこちらのコンポーネントがインタラクションを担う */} <Button> {...} </Button> </TouchArea> ただ、TouchAreaが必要になるときは上記のコードのようにインタラクティブコンテントに属する要素とセットで出てくることは仮定できます。そこで、内外を逆転して、以下のような形式でTouchAreaを実装するということも考えられます。 // buttonの本体 (スタイルを持たない) <Button {...touchAreaProps}> {/* buttonの見た目を実装 */} <ButtonInner> {...} </ButtonInner> </Button> HTMLで書くと次のようなイメージです。 <button style="all: unset; padding: 4px;"> <div style="/* ボタンのスタイルを当てる */;"> ... </div> </button> この方法の利点は、特別なテクニックを用いなくても自然にタッチエリアを拡大できることにあります。一方で、これには以下の欠点があります。 * TouchAreaのコード上の表現としては素直ではない。 * <input> や <textarea> は内部に要素を持てないため、この方法が適用できない。 特に後者の問題が致命的であり、本稿でいうところの「汎用TouchArea」のアプローチとしては選択肢から外れることになります。 Wantedlyの初期の実装ではこの方法も見られましたが、デザインシステムに合わせてUIライブラリをシステマティックに再設計する過程でこの方法は使われなくなりました。 自力でのイベントハンドリング 上記の議論から、TouchAreaは本来のインタラクティブコンテントに属する要素とは別の要素として実現する必要があります。つまり、TouchAreaに対する操作を本来の要素に対する操作として扱うために何らかの仕組みが必要になります。 その方法のひとつとして、TouchAreaに対するイベントを捕捉して適切にハンドルするという方法が考えられます。 const Button = (props) => { return ( <TouchArea onClick={props.onClick}> <button onClick={props.onClick}> {...} </button> </TouchArea> ); }; ただ、この方法にはいくつかの注意が必要です。たとえば上の例ではonClickをbuttonとTouchAreaの両方に紐付けています。これはマウス起因ではないbutton操作を適切にハンドルするために必要ですが、するとイベントバブリングによりprops.onClickが2回呼ばれてしまうことになります。 また、既知のイベント以外は委譲漏れを起こす可能性があります、たとえば内側の要素が <a> の場合、各ブラウザはCtrl+Clickや中ボタンクリック、ドラッグアウトなど様々な操作を独自に提供している場合があります。TouchAreaはこのようなカスタムの動作をスキップしてしまうことになります。 CSSの擬似クラスの対応にも工夫が必要です。JavaScriptレベルで代替となるクラスを用意するか、TouchArea要素を含めたセレクタをうまく書く必要があります。これはCSS-in-JSでコンポーネントごとにスタイルの責務を分割しようとしたときに問題が複雑化します。 この方法もWantedlyのデザインシステム実装の古いバージョンで使われたことがありますが、上記のような理由から現在は使われていません。 擬似要素による拡大 タッチエリアの拡大でよく使われるテクニックのひとつに、擬似要素を用いる方法があります。これは次のようなDOMを生成させる戦略です。 <button style="position: relative;"> <!-- CSSによる擬似要素 --> <::before style="position: absolute; top: -4px; bottom: -4px; left: -4px; right: -4px" ></::before> Click here </button> このようにすると ::before 擬似要素は親要素のcontent-boxよりも大きなboxを持つことになります。主要ブラウザではこのような場合に親要素のbox境界には関係なく子要素のboxの当たり判定を行うため、buttonの少し外側をクリックしてもバブリングによりbuttonのonClickが発火することが期待されます。 Wantedlyの場合は、タッチエリアだけを拡大するのではなく、レイアウト上の境界もあわせて拡大する必要があります。そのため、擬似要素とは別に外側にも要素を足します。 <!-- TouchAreaに対応する要素 --> <div class="TouchArea" style="position: relative; padding: 4px;"> <button style="position: static;"> <!-- CSSによる擬似要素 --> <::before style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;" ></::before> Click here </button> </div> この方法を取る場合、外側のdivと内側の擬似要素の間でサイズを同期させる必要があります。たとえば上の実装方法では、インタラクティブコンテント要素をposition: static; (デフォルトの値) で配置しています。すると擬似要素はインタラクティブコンテント要素を飛び越えて、position: relative; が指定されているTouchArea要素をレイアウトコンテナ(closest positioned ancestor)としてレイアウトされることになります。position: absolute; を指定すれば、レイアウトコンテナに対してフルサイズでサイズを決定することができます。 この方法の問題点は、やはり <input> や <textarea> に対しては動作しない場合が多いという点です。 LABELによる拡大 <button> や <input> などのフォームコントロールには、そのインタラクションの領域を拡大するための古典的な仕組みがあります。それが <label> 要素です。 <label> にはfor属性を使う方法と対象要素を囲う方法の2つがあります。対象要素を囲う場合、以下のような使い方になります。 <label> Email: <input type="text" name="email"> </label> この場合、 "Email: 〜" 全体がinput要素のラベルとみなされます。この範囲内をクリックした場合、それは当該input要素に対する何らかのアクションとみなされます。 ラベルはテキストでなければいけないわけではなく、主要ブラウザでは単にinputをlabelでラップしただけの場合でもラベルとして扱ってもらえます。つまり、この方法はデザインシステムで求められているタッチエリアの拡大の手段として利用する余地があります。 <label> が利用できる要素は決まっていて、ラベル可能要素と呼ばれています。ラベル可能要素には <input>, <textarea>, <select> などが含まれています。これはインタラクティブコンテントに属する要素のうち無または特殊なコンテントモデルを持つものをほぼカバーしているため、「擬似要素による拡大」とある種の補完関係にあるといえます。 label要素を使った方法にもいくつかの欠点があります。まず、これはあくまで「ラベル」であるため、全ての座標操作 (クリック、ホバー、タッチなど) が同じように委譲されることを要求しているものではありません。ラベルをクリックしたときにどのように振る舞うかはあくまでプラットフォームに依存して決まります。 また、labelは名前のとおりフォームコントロールの説明を付与することが想定されるため、アクセシビリティー属性のヒントに使われることがあります。タッチエリアの拡大にlabelを使う場合は説明がテキストとして含まれないため、意図しない挙動になる可能性があります。実験したところ、ブラウザによってはlabelに由来する空のテキストがplaceholderテキストなどより優先して使われてしまうことがあるようです。 HTML仕様では <label> のコンテントモデルに一定の制約を設けているため、それにも注意が必要です。特に <label> をネストさせることは禁止されているので、コンポーネント側で <label> を付与してしまうとその外でユーザー側の意思で <label> をつけるのが困難になってしまいます。このようなケースではライブラリのユーザー側で付与した <label> がコンポーネントのタッチエリアを含んでいる可能性が高いので、内側の <label> を取り除けば期待した挙動になるはずです。 2つの方法を組み合わせる 結局、筆者が調べた範囲内では、全ての要素に使える単一の優れた方法はありませんでした。そこで現在のデザインシステム実装では、最後に紹介した2つの方法を組み合わせるという手法をとっています。 内側の要素が <input> または <textarea> の場合は、 <label> を使ってタッチエリアを拡大します。ただし、呼び出し側で明示的にオプトアウトした場合は <label> のかわりに <div> を置きます。この場合は呼び出し側で要素を別途 <label> で囲うことを想定しています。 内側の要素が <input> と <textarea> 以外の場合は、擬似要素を使ってタッチエリアを拡大します。ただしこの判定はReactのレイヤで確実に行うのは難しいため、ラッパーコンポーネントは <input> / <textarea> ではないと判定します。 <input> や <textarea> をラップしたコンポーネントを指定する場合には呼び出し側で明示的に <label> による方法にオプトインする必要があります。 ブラウザ差異について HTMLやCSSは様々な環境やユーザーの事情にあわせてレンダリングされることを想定しています。CSSを使えばレイアウトを細かく正確に指定できるかのように見えますが、実際にはプラットフォームごとの事情にあわせて細部まで規定されていない部分も多く、本稿で書いたような部分の挙動は実際のブラウザに任されている面も大きいです。 したがって、本稿で書かれているような方法をとっても、全てのブラウザで同じように動作するという保証はありません。ただ、仮に本稿の手法がうまく動かないブラウザがあってもインタラクション全体が壊れることはおそらくなく、せいぜいタッチエリアが拡大されない程度の影響で済むのではないかと考えています。 まとめ * タッチエリアとレイアウト上の境界を同時に拡大する汎用コンポーネントを作りたい。 * 調べたところ、この用を達成する単一の優れた方法はなさそうだった。 * ::before 擬似要素 と label を使い分ける手法がよさそう。 今回紹介した手法がどのように動くのかを確認できるサンプルも作りました。以下で動かせます。 Wantedly, Inc.では一緒に働く仲間を募集しています * バックエンド / リーダー候補 * プロジェクトマネージャー * Webエンジニア(シニア) * 他20件の職種 * 開発 * バックエンド / リーダー候補 * Webエンジニア(シニア) * バックエンドエンジニア * フロントエンドエンジニア * エンジニアリングマネージャー * Webエンジニア * iOSアプリエンジニア * Androidアプリエンジニア * インフラエンジニア * QAエンジニア * コーポレートエンジニア * 機械学習エンジニア * UX/UI デザイナー * ビジネス * プロジェクトマネージャー * Webディレクター * PdM * 採用担当 / 人事 * 経理, 財務 * 経営企画 予実管理 管理会計 * フィールドセールス * フィールドセールス(新規営業) * PRでWantedlyを広める * BtoBマーケター HTML+CSSで作る汎用タッチエリア 原 将己 Wantedly, Inc. / Infrastructure Squad / インフラ・バックエンドエンジニア CSS HTML JavaScript 8 いいね! 8 いいね! 原 将己 Wantedly, Inc. / Infrastructure Squad / インフラ・バックエンドエンジニア コンピュータープログラムに関わるあれこれが趣味で、仕事です。 ・アルゴリズム 中高生のころにアルゴリズムのコンテストに熱中していました。 ・言語処理系・型システ... フォロー WANTEDLY ENGINEER BLOG Wantedlyのエンジニアによる、テックブログです。開発者向け技術情報を中心に発信しています。「シゴトでココロオドル人をふやす」というミッションを掲げ、ビジネスSNS Wantedly (ウォンテッドリー)を展開しています。 フォロー もっと見る 同じタグの記事 #JavaScript もっと見る 今週のランキング ランキングをみる このストーリーが気になったら、遊びに来てみませんか? 今見ているWantedlyのWebフロントエンドをもっと良くしませんか? 話を聞きに行きたい 私たちについて 運営会社 ニュース 採用情報 運営会社 ニュース 採用情報 個人向け Wantedly Visit 気軽に会社訪問 アプリを入手 Wantedly Intern 成長できるインターンと出会う アプリを入手 Wantedly People あなたの活躍を共有 アプリを入手 Wantedly Visit 気軽に会社訪問 アプリを入手 Wantedly Intern 成長できるインターンと出会う アプリを入手 Wantedly People あなたの活躍を共有 アプリを入手 ビジネス向け 採用 想いを採用の武器に サービス概要 料金表 導入事例 エンゲージメント シゴトに自律と挑戦を 社内報 チームの状態 福利厚生 採用 想いを採用の武器に サービス概要 料金表 導入事例 エンゲージメント シゴトに自律と挑戦を 社内報 チームの状態 福利厚生 返信が必要な方はこちら Wantedlyへの意見を送る 推奨環境 ヘルプ 利用規約 プライバシーポリシー 利用者情報の外部送信について 販売・運用代行プログラム -------------------------------------------------------------------------------- ©2024 Wantedly, Inc. * * *