blog

DeNAのエンジニアが考えていることや、担当しているサービスについて情報発信しています

2025.12.16 新卒研修振り返りレポート

心地よい業務アプリを実現するための実践的手法

by Koki Ueno

#frontend #ui-ux #design

はじめに

こんにちは。25卒エンジニアの、のっちー(上野航輝)です。

この記事では、DeNAの新卒エンジニア研修で新規開発した業務アプリについて、どのような観点でデザインの改善を行ったのかを具体的にご紹介します。 記事を通して、DeNAのエンジニア研修について知っていただくとともに、ソフトウェアのデザインをより良くしたいと考えている方の参考になれば幸いです!

以下のように、フロントエンドエンジニアだけでなくデザイナーにも役立つ、実践的で具体的な事例を盛り込みました。

  • 「心地よいデザイン」とは
  • 細部にまでこだわってデザインを実装する際の知見
  • 業務アプリにおけるアニメーションやレスポンシブデザインの重要性
  • 業務アプリにアニメーションを加えたり、レスポンシブ対応を進めたりする際に注意すべき点

ぜひ最後までご覧ください!

リファラル支援ツール「とりふぁ」

25卒のエンジニア研修は、4月下旬〜7月末までの約3ヶ月間行われ、そのうち6〜7月の約2ヶ月間でプロダクト開発を行いました。 プロダクト開発はA〜Fの6つの班に分かれて行い、それぞれの班に与えられたテーマに沿ってプロダクトを開発しました。

私たちのA班では、リファラル支援ツール「とりふぁ」を開発しました。

リファラル支援ツール 「とりふぁ」

リファラル支援ツール 「とりふぁ」

とりふぁは、「紹介活動の『最初の1歩』をもっと手軽に、もっと確実に」というコンセプトのもと開発しました。 とりふぁを使うことで、人事担当者は、Slackを活用してリファラル採用の依頼を行えるだけでなく、応募に至るまでの進捗状況の分析を簡単に行うことができます。

利用の流れは次の通りです。

  1. 人事がSlackを経由して社員(紹介者)にリファラルの紹介を依頼する
  2. 依頼を受けた紹介者は、社外の知人(被紹介者)と連絡を取る
  3. 被紹介者が、特定の職種を選択して応募する
  4. 人事は、見える化された情報をダッシュボードで確認し、分析する

※この記事において、とりふぁのスクリーンショットに表示された値・データは、すべて開発用のものです。

デザインへのこだわり

デザインへのこだわりは、粒度の大きなものから細かい部分までさまざまあります。 ここでは、その中でも特に意識した アニメーション・色・レスポンシブ対応 の3つについて紹介します。

アニメーション

一般的に業務アプリでは、エンタメアプリやゲームのようなリッチなアニメーションが使われることはほとんどありません。 しかし、業務アプリであっても、アニメーションにより不快感を減らしたり、ユーザーに心地よい感覚を与えることは可能です。 そこで、業務アプリの「仕事で仕方なく使わされている」という感覚を少しでも減らし、「ついつい使ってしまう」という感覚を生み出すために、アニメーションの細部までこだわりました。

では、実際に開発した「とりふぁ」の心地よいアニメーションを見てみましょう。

以下がとりふぁのダッシュボード画面です。

ダッシュボード

ダッシュボード

ダッシュボード画面では、重要な指標(最終コンバージョンレート)がアニメーション付きのグラフで表示されます。 これにより、自然にユーザーの注意を引きつけ、分析を促すことができます。

最終CVRのアニメーション

詳細画面を開くと、成果ファネルが滑らかなアニメーションで表示され、ユーザーの注目を集めます。 成果ファネルの図は、細かい数字を見るのが苦手なユーザーでも一目で客観的な情報を把握することができます。 さらに、成果ファネルにカーソルをホバーすると、離脱率の情報が表示されます。 ホバー時だけ情報を表示することで、ファーストインプレッションの図のシンプルさと、知りたい情報をすぐに得られるという体験を両立させています。

成果ファネル

成果ファネル

成果ファネルのチャートは、以下のような自作のコードにより、段階的なアニメーションを実現しました。

// ファネルチャートの段階的アニメーション
style={{
  opacity: isAnimated ? 1 : 0,
  transform: `scaleX(${isAnimated ? 1 : 0})`,
  transformOrigin: 'center',
  transition: 'transform 600ms cubic-bezier(0.2, 0.0, 0, 1.0), opacity 300ms ease-in-out',
  transitionDelay: `${index * 100}ms`, // 100msずつ遅延
}}

ダッシュボードにおけるリッチなアニメーションは意外に好評で、業務アプリであっても、心地よいユーザー体験の重要性を感じました。 また、心地よいアニメーションは丁寧な印象を与え、それだけでユーザーからの信頼の獲得に寄与すると考えています。 ここでの重要な感覚は「いたずらに手が込んでいる」ということではなく、「丁寧に手入れが行き届いている」と感じてもらえる点です。

UIにおけるアニメーションには、大きく2つの役割があります1

  1. 表示の不快感を減らす: マイナスの状態をゼロに近づける
  2. ユーザーを楽しませる: プラスの要素を追加する

多くのユーザーは、スマホでYouTubeを再生・一時停止したときに、どのようなアニメーションが動いているのか思い出すことができません。 仮にアニメーションを思い出せたとしても、通常動画とYouTubeショートでアニメーションが異なる2ということを知っているユーザーはほとんどいないでしょう。

なぜなら、これらのアニメーションはあまりにも自然で違和感がなく、主張することなくアプリに溶け込んでいるからです。 しかし、「主張しない」ということは、アニメーションを全く使わないという意味ではありません。 むしろ、アニメーションを使っているからこそ、自然なUIが実現されています。

もしアニメーションを一切使わない場合、表示が切り替わるたびに、大きな表示の変化が生まれます。 これが不快感を発生させます。 そこで表示の切り替え前後にアニメーションを加えることで、違和感を減らすことができます。

つまり、不快感を消すアニメーションは、動いているにもかかわらず、ユーザーの意識から消えます。 むしろ「気づかれないこと」こそがゴールです。 だからこそ、YouTubeの再生・一時停止アニメーションが記憶に残らないのは当然だといえます。

補足すると、表示の不快感を減らすアニメーションは、必要なUIを見えにくくしてユーザーの認識を阻害しているのではありません。 むしろ必要なUIの周りにある"不要なストレス"を減らすことにより、ユーザーの集中力をより重要な部分に向けさせるための工夫です。 そのため、奇抜で派手なアニメーションではなく、アプリと調和するような一見シンプルなアニメーションを用いるのです。 その結果、より使いやすいアプリが実現されます。

一方で、“ユーザーを楽しませる"タイプのアニメーションは、ユーザーの興味を惹きつけ、記憶に残すことを目指しています。 YouTubeの「いいね」やチャンネル登録時に表示される、カラフルでリッチなアニメーションはその代表例で、多くのユーザーが思い出せるはずです。

YouTubeのデザインについては、Google Designの記事でも詳しく紹介されています。 ぜひご覧ください。

「とりふぁ」はエンタメアプリではないため、不快感を減らすアニメーションを基本としつつも、ダッシュボードのような一部の画面では、リッチなアニメーションを使ってバランスを取るように調整しています。

続いて、人事が社員に対してリファラル採用の協力を依頼するための画面を見てみましょう。 この画面では、社員をカテゴリーごとに検索することで、依頼を送信する対象者を簡単に選択することができます。

送信画面

送信画面

絞り込み検索では、条件ボタンのON/OFFの状態がアニメーションによりスムーズに切り替わります。 また、対象者一覧は画面下部に滑らかに表示され、ストレスなく表示が切り替わるようになっています。

最後に、リファラル応募ページの編集画面のアニメーションを紹介します。

前提として、私たちの班では以下のようなリファラル応募ページを作成しました。 これは、社外の方が職種を選択して応募するためのページです。

リファラル応募ページ

リファラル応募ページ

このページに表示されている文章や応募リンクなどの掲載内容は、人事の管理画面から自由に編集できます。 その人事向けの編集機能が、以下のような画面です。

応募ページの編集画面

応募ページの編集画面

この画面では、操作が完了するたびに、右上にSnackbarによるメッセージが表示されます。 Snackbarにアニメーションをつけることで、ユーザーの注意を集めつつも、通常の操作を邪魔することなく表示することができます。

また、それぞれの編集項目についても、編集モードがONとOFFで切り替わる際に、アニメーションにより滑らかに表示が切り替わるように実装しました。

アニメーションの統一感

とりふぁアプリ全体にわたってアニメーションの統一感を保つために、基本的に共通のイージング(アニメーションカーブ)を採用しました。

Material Designの モーションガイドライン(Easing and duration) に書かれている通り、イージングは cubic-bezier(0.2, 0.0, 0, 1.0) を基本として実装しました。

基本のイージング

基本のイージング

このイージングでは、アニメーションの終わりがゆったりとした動きになるため、丁寧で心地よい印象を与えることができます。

心地よいイージング

心地よいイージング

意図的な個別化

アニメーションの設定値は、何でも共通化すればよいというものではありません。 特にduration(アニメーションの持続時間)については、敢えて共通化していません。

モーションガイドライン(Choosing a duration) に書かれているように、アニメーションの持続時間は、アニメーション領域の大小やユーザーの意図に応じて変更し、それぞれ適切な値を選択することが推奨されています。

例えば、チェックボックスのアニメーションに3秒もかかっていたら、もたもたした印象を与える一方で、画面全体のアニメーションが短時間で終わると、忙しない印象でユーザーに不快感を与えてしまいます。 これはなぜでしょうか?

不自然なアニメーション

不自然なアニメーション

そもそも物理学には、同じ力で操作するなら「軽いものほど速く動き、重いものほどゆっくり動く」という基本的な前提があります。 これは私たちが日常的に当たり前の感覚として持っているものです。 (加える力が異なるため例えとして完全に正確ではありませんが、)人間が全力で走ってトップスピードに達するまでは数秒しかかかりませんが、新幹線が最高時速に到達するまでには少なくとも数分かかります。 したがって、自然なアニメーションを実現するなら、そのアニメーションが動く面積に応じて、durationの値を個別に調整する必要があります。 しかし、エンジニア側の都合で「コードの共通化」を優先し、全アニメーションの duration を一律にしてしまうと、かえって不自然な動きが生まれてしまいます。

自然なアニメーション

自然なアニメーション

もう1つの観点は、ユーザーの意図や期待です。 例えば動画・音楽の再生や停止には全くラグがないように気をつける必要があるため、もしアニメーションを用いる場合は、コンテンツの再生・停止は即座に反映されつつ、それとは別でアイコンのアニメーションを動かすように実装します。 また、チェックボックスのように状態を直接操作するUIも、アニメーションを0.3秒以内にとどめることで、違和感・不快感を減らす工夫をします。 逆に、ユーザーの操作とは関係なく再生されるアニメーションは、比較的長い時間でも不快感は生まれません。

とりふぁでは、コンポーネントの大きさやユーザーの意図に応じて、durationを300msから1000msを超えるものまで、個別に細かく調整しています。

ローディング表示

最後に、ローディング表示へのこだわりについて紹介します。

ローディングでは、以下のアニメーションを表示しています。

ローディングアニメーション

ローディングアニメーション

ただ、このローディングアニメーションが一瞬だけ表示された場合、ユーザーには「画面がチラついた」という印象を与えてしまいます。

ローディングが一瞬表示される例

ローディングが一瞬表示される例

そこで、以下のような LoadingImage コンポーネントを作成して解決しました。

export function LoadingImage({ size = 80, className = '' }: LoadingImageProps) {
  return (
    <div
      className={`inline-flex items-center justify-center ${className}`}
      style={{
        width: size,
        height: (size * 32) / 40,
        opacity: 0,
        animation: 'fadeInLoading 300ms ease-in-out 200ms forwards',
      }}
    >
      <Image
        src='/torifa-loading.svg'
        alt='読み込み中'
        width={size}
        height={(size * 32) / 40}
      />
    </div>
  )
}

LoadingImage では、ローディングが表示されてもすぐには内部の画像が表示されず、200ms後にフェードインで表示されるようになっています。 これにより、ローディング時間が200ms未満の場合、画像が一瞬だけ見えて画面がチラつくという問題が起きません。

ちなみに、この200msという値は、Material Designのガイドラインにおける、 Loading indicatorのページ を参考にしています。

▼ Material Designのガイドラインより

待ち時間 推奨
〜 200ms ローディング表示なし
200ms 〜 5s ローディングインジケーター
5s 〜 プログレスインジケーター

ガイドラインでは、ローディングが5秒を超える場合は、ローディングの進捗を表示することが推奨されています。 しかし、とりふぁにおいてローディングに非常に時間がかかる画面はないため、単に200msで表示を切り替えています。

とりふぁでは、以下のように、それぞれのタグに固有の色を設定しています。

色のついたタグ

色のついたタグ

ここで、「yy卒」のようなタグはサービスを運用するにつれて増えていくことが想定されます。 そのため、タグの色は、タグのラベル文字列から自動で生成されるような仕組みを構築しました。

ラベルの色が満たすべき条件は以下の通りです。

  1. 同じ文字列からは常に同じ色を生成
    • ランダムに色を生成するわけではない
  2. 可能な限りタグの色を分散させる
  3. 視認性の良い配色を生成

ラベル文字列からの色生成

条件の1つ目および2つ目を満たすために、実装方法を検討します。

1つ目の条件を満たすには、ランダム関数を用いずに、ラベルの表示文字列からカラーコードを生成する必要があります。 そこで、簡易的なハッシュ関数を実装すればOKです。

// 文字列から簡易ハッシュ値を計算
let hash = 0
for (let i = 0; i < text.length; i++) {
  const char = text.charCodeAt(i)
  hash = (hash << 5) - hash + char
  hash = hash & hash // 32bit整数に変換
}

しかし、この実装では2つ目の条件を満たすことができないという問題があります。

例えば、ラベルが タグAタグB の場合を考えます。 すると、以下のように最初の2文字の文字コードは共通しているため、最終的なハッシュ値は最後の文字の文字コードの差しか影響を受けません。

charCodeAt タ :  12479
charCodeAt グ :  12464
charCodeAt A  :  65
charCodeAt B  :  66

実際にコードを実行すると、 タグAタグB それぞれのハッシュ値は以下のようにわずか1しか差がありません。

  • タグA12378768
  • タグB12378769

この問題を解決するため、軽量かつより高い分散を実現できるFNV-1aハッシュアルゴリズムを内部で使用しています。 FNV-1aハッシュを用いることで、条件の1および2を満たす実装ができます。

let hash = 2166136261 // FNV offset basis
for (let i = 0; i < text.length; i++) {
  const char = text.charCodeAt(i)
  hash ^= char
  hash = (hash * 16777619) >>> 0 // FNV prime, 32bit符号なし整数に変換
}

Material Color Utilities による動的色生成

最後に残った条件「視認性の良い配色を生成すること」を実現するために、Googleの Material Color Utilities を利用しました。

import { Hct } from "@material/material-color-utilities"

export function generateColorFromText(text: string, tone: number): string {
  if (!text) return "#6B7280" // gray-500相当

  // FNV-1aハッシュ
  let hash = 2166136261
  for (let i = 0; i < text.length; i++) { ... }

  // ハッシュ値からhue(0-360)を生成
  const hue = hash % 360

  // chromaを共通化
  const chroma = 48

  // HCT色空間からARGB値に変換
  const hct = Hct.from(hue, chroma, tone)
  const argb = hct.toInt()

  // ARGBからHEXに変換
  const hex = `#${(argb >>> 0).toString(16).padStart(8, '0').slice(2)}`

  return hex
}

選択状態に応じた動的色調整

generateColorFromText()を使うことで、アプリ全体で簡単にタグの配色を利用することができます。

export function generateTagColors(tag: string) {
  const bgColor = generateColorFromText(tag, 93) // 明るい背景
  const textColor = generateColorFromText(tag, 40) // 暗い前景

  return { bgColor, textColor }
}

これらの仕組みにより、デザイナーが手動で色を指定しなくても、視認性の高いタグの配色が自動的に生成されるようになりました。

自動生成されたタグの配色

自動生成されたタグの配色

レスポンシブ対応

業務アプリは、ユーザーがPCの画面の半分のスペースで使おうとしても、横幅を狭めると表示が崩れてしまうために、業務効率が低下するということが起こりがちです。 この背景には、業務ツールは、SNSやエンタメアプリなどに比べ、スマホで利用される割合が低いということが挙げられます。 そのためレスポンシブ対応が後手後手に回りやすく、PCの半分程度の横幅でも、レイアウトが崩れてしまうということが多々あります。

しかし、とりふぁでは極力レスポンシブ対応を行い、主要な画面では横幅が300px以下でも表示が崩れないことを確認するようにしています。

横幅300pxで表示した画面の例

横幅300pxで表示した画面の例

送信画面

レスポンシブ対応の例として、リファラル依頼送信画面における、対象者選択のデザインを詳しく紹介します。

レスポンシブ対応は通常、タブレットやスマホでの表示を想定して行われるため、横幅のみをチェックすればOKだと考えられがちです。 しかし、実際にはPCであっても、画質の低いモニターで表示したり老眼などの理由で表示サイズを拡大して利用するユーザーがいることも想定されます。 その場合、エンジニアの動作確認では問題なく操作できると思い込んでいても、実際には縦の表示領域が狭いために、使いにくいUIになってしまう可能性があります。

以下は、対象社員を選択する機能の動作確認をした際のスクリーンショットです。 1枚目の画像では、エンジニアが4Kモニターなどの広い画面で動作確認をした場合で、問題なく対象者が表示されているように見えます。 ところが、2枚目の画像のように、ユーザーの利用環境によっては、スマホで操作しているわけではなくとも、画面の縦の長さが短いため対象者が何人選択されているのか全くわからないということが起こります。

エンジニアの動作確認

エンジニアの動作確認

動作確認で見落としがちな環境での見え方

動作確認で見落としがちな環境での見え方

この問題を解決するため、対象者一覧を画面下部に固定することで、対象者が何人選択されているのかを常に確認できるようにしました。

対象者一覧を画面下部に固定

対象者一覧を画面下部に固定

しかも、画面をスクロールして次のセクションが表示されると、対象者一覧は自動で画面外に移動します。 これにより、固定表示による情報の見やすさと、ユーザーの操作を邪魔しない使いやすさを両立させることができます。

対象者一覧が画面外に移動

対象者一覧が画面外に移動

以下のコードは、対象者一覧を画面下部に固定するためのコードです。 position: stickyを指定することで、対象者のセクションでは画面下部に表示される一方で、対象者セクションが画面外に移動した際に、同時に対象者一覧が画面外に移動するようになっています。

{
  /* 対象者一覧 */
}
<AnimatedHeight
  ...
  className={`...
    sticky bottom-[88px] // position: stickyを指定
    ${targetEmployees.length > 0 ? "opacity-100" : "opacity-0"}
  `}
>
  {targetEmployees.length > 0 && (
    {/* 選択された対象者の表示 */}
    <div className="...">
      {targetEmployees.map((employee) => (
        <div
          key={employee.id}
          className="..."
        >
          <Avatar ... />
          <span className="...">{employee.name}</span>
          <IconButton ... />
            <Icon name="cancel" ... />
          </IconButton>
        </div>
      ))}
    </div>
  )}
</AnimatedHeight>

エンジニアは恵まれた環境で動作確認をしがちだという話

エンジニアは、往々にして4Kモニターのような高画質かつ巨大な画面で動作確認をしがちです。 しかしながら、実際のユーザーは必ずしも外部モニターで業務を行うわけではなく、数年前のPCを使い続けている可能性もあります。

エンジニアが気をつけるべきこととして、デザインのチェック時にはあえて普段と異なる環境で動作確認を行うということが挙げられます。 動作確認では、表示するサイズを変えたり画面の明るさを変えたりして、使いやすい機能になっているかを確認することが重要です。

少し話は逸れますが、このように低画質かつ小さな画面で見栄えを確認すべきものとして、アイコンのデザインが典型的です。 アイコンのデザイン時には、しばしば4Kモニターで確認して満足していても、実際にアプリのアイコンとしてファビコンなどに設定してみると、非常に小さくて見えにくいということがよく起こります。

とりふぁのアイコンも、小さく表示した際に問題なく視認できるかどうかを確認しました。

とりふぁのアイコン とりふぁのアイコン とりふぁのアイコン

おわりに

最後までご覧いただきありがとうございました。

とりふぁでは、業務アプリで起こりがちな以下の課題の改善に積極的に取り組みました。

  • シンプルだが、味気ない印象を受ける
  • 表示されている情報が多すぎて、数字を見るのが辛い
  • 情報が追加されるたびに色を設定する手間が面倒
  • 画面サイズを少し変えただけで使いにくくなる

これら多くの課題に取り組むためには、今回紹介したように、アニメーション・色・レスポンシブ対応のように要素を分割して考えることが重要です。 それぞれの観点から修正を行うことで、見落としなく、かつ網羅的にデザインを改善することができます。

その結果、心地よくポジティブな印象をユーザーに与えつつ、注目すべき情報ややるべき操作に集中して利用できるアプリを実現することができました。

部署配属後も、デザインの細部までこだわる姿勢で業務に取り組みたいと考えています。 本記事が、プロダクトのデザインをより良くしたいと考えている方々に少しでも役立てば幸いです。

私たちの25卒A班では、技術的負債の観点から「良いソフトウェア」についての記事も執筆していますので、ぜひそちらもご覧ください!


  1. 他にも「ユーザーの視覚的な理解を助ける」ことや「アラートのような重要な要素に注目を集める」などの観点がありますが、今回の記事では省略します。 ↩︎

  2. 記事執筆(2025年9月)時点。もし良かったらスマホでYouTubeを確認してみてください。 ↩︎

最後まで読んでいただき、ありがとうございます!
この記事をシェアしていただける方はこちらからお願いします。

recruit

DeNAでは、失敗を恐れず常に挑戦し続けるエンジニアを募集しています。