見出し画像

夢の、デザイナー向け React ワークショップ Q

巻頭歌

デザイナーよデザイナーよ何故躍る
フロントエンドの実装がわかっておそろしいのか

序章、または終章

「…………ブウウ――――――ンンン――――――ンンンン………………。」

Sketch ファイルを開くと、PC は蜜蜂の唸るようなファンの音を鳴り響かせた。

……早く作業を進めねば……。

幸いなことに PM から受け取ったワイヤーフレームの詳細度は高い。あとは色を塗るだけだ。
ワイヤーの構成をなぞり、パレットから選定した色をポツポツ載せていくと、数分で作業は完了した。
すぐさま InVision にアップロード。Slack で開発チームにデザインの完成を伝えてソレッキリだ。
アーロンチェアに身を深く沈め、私は眼を閉じた。

…………。

「スッコココココ」

私がウスウスと眼を覚ました時、小気味よい通知音が耳朶に響いた。
ノロノロ Slack を開くと、メッセージが大量に届いている。

『……デザイナーさま……デザイナーさまデザイナーさまデザイナーさま。
なぜ……なぜ返事をして下さらないのですか。
あたしです、あたしです、あたしですあたしです。デザイナーさまはお忘れになったのですか。
妾(あたし)ですよ。あたしですよ。プロジェクトのフロントエンドエンジニアだった……妾……妾をお忘れになったのですか。
……妾はデザイナー様と御一緒になる前の晩に……Goodpatch 式 OOUI ワークショップの前の晩の真夜中に、デザイナー様のお手にかかって死んでしまったのです。
……それがチャント生き返って……お墓の中から生き返ってここに居るのですよ。幽霊でも何でもありませんよ……デザイナーさまデザイナーさまデザイナーさまデザイナーさま。……ナゼ返事をして下さらないのですか……デザイナー様はあの時の事をお忘れになったのですか……』

私はヨロヨロと背後に蹌踉いた。モウ一度眼を皿のようにしてそのメッセージを凝視した……。

『……デザイナーさまデザイナーさまデザイナーさま。何故、御返事をなさらないのですか。妾がこんなに苦しんでいるのに……タッタ一言……タッタ一言……御返事を……』

……何という奇怪な現象だ。

私はこのフロントエンドエンジニアを知っている。
件のワークショップでデザイナーと PM 間の共通認識を作る目的をより強めるため、邪魔をされないようこの手にかけたのだ。
それが、何故……。

『……デザイナーさま。デザイナーさま。デザイナーさまデザイナーさまデザイナーさまデザイナーさまデザイナーさま。
あの、Goodpatch さま主催の素敵なワークショップで一所懸命に OOUI を学んだといふのに、何故、何故、ワイヤーに色を塗っただけの成果物をデザインと呼んでしまうの。
何故、UI クラス図を書いた経験が成果物に活かされていないの。何故、データモデルとしてインターフェースを捉えられないの。何故、何故、何故なのデザイナーさま。
デザイナーさまデザイナーさまデザイナーさまデザイナーさまデザイナーさま。……モウ一度……デザインのお考えを……聞かしてエ――ッ…………』

そのメッセージはトテも人間のコミュニケーションとは思えないほど嗄れてしまって、ただ、底悲しい、痛々しい響ばかりが、画面を通して伝わってくるのであった。

アア……ソウダッタ。
あのワークショップは、ほんのきっかけに過ぎなかったのだ。
ワークショップを受け、そこからどんなふうに業務に活かしていくか、それを考えるきっかけに過ぎなかったのだ。
それなのに、ワークショップを受けただけで気をよくした私は普段の行いを何も改善することなく、ただいつも通りの見た目だけの成果物を出してしまっていた……。
……これはどうした事なのだ。何という不思議な、何という馬鹿げた事だろう。アハ……アハ……可笑しい可笑しい……アハアハアハアハアハ……。
……ああ苦しい。やり切れない。俺はどうしてコンナに可笑しいのだろう。アッハッハッハッハッハッハッ……。
私は疲れた。狂いくたびれて、考えくたびれた。
自分が突立っているのか、座っているのか……いつ……何が……どうなったやらわからない無意識状態に、ズンズン落ち帰って行った……。

幕間

ああア――アア――あああ。右や左の御方様へ。旦那御新造、紳士や淑女、お年寄がた、お若いお方。お立ち会い衆の皆さん諸君。トントその後は御無沙汰ばっかり。

SmartHR デザイナーのおうじです。
冒頭の元ネタがわからんと隣席の PM に不審がられましたがめげずに書いています。

先日 Goodpatch 様に講義頂いたワークショップも記憶に新しいですが、 SmartHR プロダクトデザイングループでは現在絶賛デザインプロセスを模索中です。
今日はその一環として行った「デザイナー向け React ワークショップ」の内容をお伝えしようと思います。

画像1

PM やデザイナーの業務領域からインターフェースの構造を特定する手法が前回のワークショップとするならば、実装観点から構造を特定しようというのが今回の目論見です。
幸いなことに我々には SmartHR UI という公開中の React コンポーネントライブラリがあるので、本ワークショップでは同ライブラリを題材にしました。

お賞めなされて下さるならば。私の喜び天井知らずじゃ……チャカチャカポコポコポコポコチャカチャカポコポコ……

本編一章 「準備」

今回はまだ SmartHR UI の実装をやったことがないデザイナーと PM が対象のため、事前に時間を取って開発環境の構築を行いました。
SmartHR UI では以下のツールの設定を行う必要があります。

画像2

※時間短縮のため、今回は git の設定をスキップしました

また、同時に SmartHR UI に関わる技術についてもざっと解説を行いました。

画像3

本編二章 「実践」

まずは、要件を眺めながら Sketch を使ってコンポーネントの「見た目」を作ってみます。
要件を満たすには大体このくらいのバリエーションのボタンができるはずです。

画像4

デザイナーにとっては普段から行っている作業ですが、いざ「ボタンの要件」のみと顔を突き合わせてみると、「押した際のアクション」や「表示が変わる条件」といった静的な見た目だけでは表現できない要件が複数あることがわかります。
これらの要件もフロントエンドでの実装が想像できればエンジニアとスムーズにコミュニケーションしていけそうですね。

画像5

次からは実際に React を書く上で必要となってくる情報の設計と実装をしていきます。

コンポーネントの入力値である「プロパティ」について考えてみます。

画像6

プロパティは「名前+値」のセットで表現されるモノの構成要素です。
Sketch シンボルの Override パネルを想像してもらえればわかりやすいかと思いますが、コンポーネントもインスタンス化される際にいくつか値を引数として受け取ることになるので、入ってくるであろう値の「型」を指定しておく必要があります。

この型をコードで表現すると以下のようになります。

// SampleButton.tsx

import React, { FC } from 'react'
import styled, { css } from 'styled-components'
import { useTheme, Theme } from '../../hooks/useTheme'

type Props = {
 label: string
 icon?: React.ReactNode
 type?: 'primary' | 'secondary'
 onClick?: () => void
 disabled?: boolean
}

~省略~

※ label は children で指定するのが通例ですが、前段のプロパティの考え方と一致させるためこの書き方にしています

これでコンポーネントの構成要素が大体つかめてきました。
最後に、プロパティを受け取り、プロパティの値によって変化する出力部分を考えます。

画像7

プロパティを「型」で指定したことからもわかるように、コンポーネントはインスタンス化される際になにかしらの指定を受けます。
どんなデータを受け取り、それらによってどのように様相を変えて出力していくかを定義していきます。

まずは出力にプロパティを流し込むことろまで書いてしまうと、以下のようになります。

// SampleButton.tsx

~省略~

export const SampleButton: FC<Props> = ({
  label = '',
  icon,
  type = 'secondary',
  onClick,
  disabled = false,
}) => {
  const theme = useTheme()

  return (
    <Wrapper themes={theme} className={type} onClick={onClick} disabled={disabled}>
      {icon && icon}
      {label}
    </Wrapper>
  )
}

ほとんどのプロパティは JSX に流し込むだけで element の基本要素として機能しますが、今回は「プライマリかセカンダリの見た目を選択できる」という要件があります。
見た目の指定は className で受け取るようにしたので、あとはそれぞれの装飾を styled component に定義してあげます。

// SampleButton.tsx

~省略~

const Wrapper = styled.button<{ themes: Theme }>`
${({ themes }) => {
  const { size, palette } = themes

  return css`
    display: inline-block;
    box-sizing: border-box;
    height: ${size.pxToRem(40)};
    padding: 0 ${size.pxToRem(size.font.TALL)};
    margin: 0;
    border: none;
    border-radius: ${size.pxToRem(40)};
    font-size: ${size.pxToRem(size.font.TALL)};
    font-weight: bold;
    cursor: pointer;

    &[disabled] {
      opacity: 0.5;
      cursor: not-allowed;
    }

    &.primary {
      background-color: ${palette.MAIN};
      color: #fff;
    }

    &.secondary {
      border: 1px solid ${palette.BORDER};
      color: ${palette.TEXT_BLACK};
    }
  `
}}
`

※見た目ごとの class があるより styled component に props を渡して分岐処理するほうが上書きされ難いので安全ですが、ワークショップではデザイナーが書きやすいであろう方向に振り切りました

以上でだいたい完成です。
storybook で表示を確認すると以下のようになりました。

画像8

最後に今回書いたコードサンプルを貼っておきます。

import React, { FC, ReactNode } from 'react'
import styled, { css } from 'styled-components'
import { useTheme, Theme } from '../../hooks/useTheme'

type Props = {
  label: string
  icon?: ReactNode
  type?: 'primary' | 'secondary' | 'grandpa'
  onClick?: () => void
  disabled?: boolean
}

export const SampleButton: FC<Props> = ({
  label = '',
  icon,
  type = 'secondary',
  onClick,
disabled = false,
}) => {
  const theme = useTheme()

  return (
    <Wrapper themes={theme} className={type} onClick={onClick} disabled={disabled}>
      {icon && icon}
      {label}
    </Wrapper>
  )
}

const Wrapper = styled.button<{ themes: Theme }>`
${({ themes }) => {
  const { size, palette } = themes

  return css`
    display: inline-block;
    box-sizing: border-box;
    height: ${size.pxToRem(40)};
    padding: 0 ${size.pxToRem(size.font.TALL)};
    margin: 0;
    border: none;
    border-radius: ${size.pxToRem(40)};
    font-size: ${size.pxToRem(size.font.TALL)};
    font-weight: bold;
    cursor: pointer;

    &[disabled] {
      opacity: 0.5;
      cursor: not-allowed;
    }

    &.primary {
      background-color: ${palette.MAIN};
      color: #fff;
    }

    &.secondary {
      border: 1px solid ${palette.BORDER};
      color: ${palette.TEXT_BLACK};
    }
  `
}}
`

出力される <button> という要素に対してプロパティをどのように割り振りどこに処理を入れるのか、実際にやってみると HTML 要素や JavaScript の基本知識がかなり求められることがわかります。
デザイナーがコードを書かないにしても、デザインで構造を表現するのであれば、ある程度実装のリテラシーは必要になってきそうです。

画像9

本編三章 「まとめ」

以上、約二時間と駆け足のワークショップでありましたが、参加したメンバーからは「デザインとコードの関連性が見えてきた」との声があがっていました。

SmartHR UI の Sketch 構造とProps とComponentの出力の関係性がよくわかった。
Props を考えてから Sketch で作り出すのも理解できたし、デザインするときに考えている事が一致する部分もあれば考慮できてない部分もあるのでそこに気づけてよかった。
プロパティとスタイル指定の関係がなんとなく理解できて、今後のSketchデータ作成時に関係を意識できそうです

コンポーネントを実装の側面から見ると、「見た目」のデザインはプロダクト開発においてどの位置にあるのかがわかってきます。
コンポーネントは入力値であるプロパティと、出力値を持っているのであれば、デザインは出力された「結果」を表現したものになるのではないでしょうか。

通常の開発プロセスではコンポーネント実装の前にデザイン制作が行われることが多く、エンジニアはデザインという「結果」からコンポーネントの入出力をリバースモデリングしています。
デザインの段階から入出力を想定し、実装との解釈に差がなくせれば、よりスムーズに開発も進められそうですね。

終章、または序章

……ブ…………ンンンンン……

『……デザイナーさま……デザイナーさまデザイナーさまデザイナーさま。
なぜ……なぜ返事をして下さらないのですか。……デザイナーさまデザイナーさまデザイナーさま。』

……オオ……フロントエンドエンジニアの声が聞こえる……
…… Slack の画面ではなく……耳にはっきりと……

……ブ――――ン……

『……デザイナーさま。デザイナーさま。デザイナーさまデザイナーさまデザイナーさまデザイナーさまデザイナーさま。たとえお返事をくださらなくとも、最後にこれだけは言わせてください。

……現在 SmartHR では UI デザイナーを募集しております。

プロダクトデザイングループはコンポーネント指向で設計する集団です!デザインもエンジニアリングも両方のリテラシーを活かせる素敵な職場です!!』

……ブ――ンブ――ンブ――ンブ――ンブ――ン……

フロントエンドエンジニアの語気が強くなると同時にパッと視界が開け、見慣れたバナーが目に飛び込んだ。
それはまぎれもなく

「……アッ…… Wantedly のリンク……」

叫ぶ間もなく、私の意識は再び暗い底へと落ちていった。

……ブウウウ…………ンン…………ンンン…………。


この記事が気に入ったらサポートをしてみませんか?