CSSでkintoneアプリをマテリアルデザインにする

著者名:shimo3( サイボウズ株式会社 (External link)

目次

はじめに

この記事では、CSSでkintoneアプリの見た目をデザインする方法を紹介します。
今回は、CSSでマテリアルデザインを表現してみました。

マテリアルデザインとは、UIをカードなどのリアルな物体に見立てることで、直感的な操作性を演出するデザインの手法です。

CSSの基本的な書き方を理解している方が、アプリへ適用する際の参考になるよう、具体的なサンプルコードとその解説を提供します。

また、kintone全体の見た目のカスタマイズを行う場合は kintone全体のカスタマイズ (External link) を参照してください。

完成イメージ

まずは、完成したデザインのイメージを確認しましょう。
レコード内の詳細情報エリアをカード状にあしらうことで、マテリアルデザインを表現しています。

カスタマイズを適用するアプリ

今回カスタマイズするアプリは、kintoneのアプリストアからインストールできる「案件管理(営業支援パック)」です。
アプリストアから 営業支援パック (External link) を、サンプルデータを含めるにチェックを入れた状態でインストールしてください。

サンプルコード

caution
警告

ここで紹介する例では、kintoneのクラス名を指定しています。
kintoneのアップデートの際に、影響を受けて動かなくなる可能性があります。
注意事項

カスタマイズで使用するソースコードを紹介します。
見た目を変更するためのCSSファイルに加え、DOM操作するためのJavaScriptファイルも作成します。

JavaScript

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/*
 * Material design sample program
 * Copyright (c) 2024 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
*/

// 既存の DOM に付与する独自の クラス名を設定
const subTableClass = '.value-5517967'; // 関連レコード一覧フィールドに採番されている クラス名を指定
const classesToAdd = [
  {targetDom: '.container-gaia', add: 'md-container'},
  {targetDom: '#record-gaia', add: 'md-record'},
  {targetDom: '.layout-gaia', add: 'md-card-wrapper'},
  {targetDom: '.row-gaia', add: 'md-row'},
  {targetDom: '.control-gaia', add: 'md-column'},
  {targetDom: '.control-label-gaia', add: 'md-label'},
  {targetDom: '.control-value-gaia', add: 'md-value'},
  {targetDom: '.control-design-gaia', add: 'md-value--blanc'},
  {targetDom: '.control-hr-field-gaia', add: 'md-column--hr'},
  {targetDom: '.subtable-gaia', add: 'md-subtable'},
  {targetDom: '.subtable-label-gaia', add: 'md-subtable-label'},
  {targetDom: subTableClass, add: 'md-value--subtable'}
];

(() => {
  'use strict';

  const applyMaterialDesignStyles = (classesToAdd) => {
    // 既存の DOM に 独自のクラス名を追加
    classesToAdd.forEach(item => {
      document.querySelectorAll(item.targetDom).forEach(element => {
        element.classList.add(item.add);
      });
    });

    // カードのスタイルを適用するために、既存の DOM を独自の DOM で囲う
    document.querySelectorAll('.md-card-wrapper').forEach((wrapper) => {
      const card = document.createElement('div');
      const scrollBox = document.createElement('div');
      const scrollBoxInner = document.createElement('div');

      card.classList.add('md-card');
      scrollBox.classList.add('md-card-scroll');
      scrollBoxInner.classList.add('md-card-scroll-inner');

      // .md-card-wrapper 内にあるすべての子要素 DOM を scrollBoxInner の直下に移動
      while (wrapper.firstChild) {
        scrollBoxInner.appendChild(wrapper.firstChild);
      }

      // scrollBoxInner の DOM を scrollBox の直下に移動
      scrollBox.appendChild(scrollBoxInner);

      // scrollBox の DOM を card の直下に移動
      card.appendChild(scrollBox);

      // card の DOM を wrapper の直下に移動
      wrapper.appendChild(card);
    });
  };

  kintone.events.on([
    'app.record.detail.show',
    'app.record.create.show',
    'app.record.edit.show'
  ], (event) => {
    applyMaterialDesignStyles(classesToAdd);
    return event;
  });
})();

CSS

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
 * Material design sample program
 * Copyright (c) 2024 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
 */
@charset "UTF-8";

/* 【----- アプリ全体のスタイル -----】 */
.md-container .md-record#record-gaia {
  font-size: 16px;
  padding: 22px;
}

.md-container .md-card-wrapper.layout-gaia {
  width: 100% !important;
  box-sizing: border-box;
}

/* 【----- カード部分のスタイル -----】 */
.md-card {
  max-width: 910px;
  margin-inline: auto;
  padding-block: 40px;
  background-color: #fff;
  border-radius: 10px;
  box-shadow: 2px 0 1px rgba(0, 0, 0, .05),
              -2px 0 1px rgba(0, 0, 0, .05),
              0 2px 1px rgba(0, 0, 0, .05),
              0 -2px 1px rgba(0, 0, 0, .05);
}

/* 【----- カードの内側のスタイル -----】 */
.md-card-scroll {
  overflow: auto;
  width: 100%;
}

.md-card-scroll-inner {
  width: 910px;
  display: grid;
  grid-template-columns: 1fr;
  grid-gap: 36px 0;
  padding-inline: 40px;
  box-sizing: border-box;
}

/* 【----- カードの内の行のスタイル -----】 */
.md-row.row-gaia {
  margin: 0;
  display: flex;
  gap: 16px;
}

.md-row.row-gaia::after {
  content: none;
}

/* 【----- カードの内のカラムのスタイル -----】 */
.md-row.row-gaia .md-column {
  padding: 0;
}

.md-row.row-gaia .md-column.control-gaia.control-label-field-gaia {
  padding: 0;
  margin: 0;
  background-color: transparent;
}

.md-column--hr {
  width: 100% !important;
  padding-block: 20px;
}

.md-label.control-label-gaia {
  padding: 0;
  margin: 0 0 12px 0;
  background-color: transparent;
  font-size: 16px;
  font-weight: 600;
}

.showlayout-gaia .md-row.row-gaia .md-value.control-value-gaia {
  border: solid 0 transparent;
  padding: 6px 12px;
  background-color: #f5f5f5;
  border-radius: 6px;
}

.md-value + .md-value--blanc {
  display: none;
}

/* 【----- 関連レコード一覧のスタイル -----】 */
.showlayout-gaia .md-row.row-gaia .md-value.md-value--subtable.control-value-gaia {
  padding: 0;
  overflow: hidden;
  position: relative;
  border: solid 1px #e3e7e8;
}

.md-subtable.subtable-gaia td:first-child,
.md-subtable.subtable-gaia td:last-child {
  border-right: none;
}

.md-subtable.subtable-gaia tr:last-child td {
  border-bottom: none;
}

.md-subtable-label {
  font-size: 14px;
  font-weight: 600;
}

サンプルコードの解説

サンプルコードの主なポイントについて詳しく説明します。

JavaScriptの主なポイント

まずは、既存のDOMに独自のクラス名を付与しています。
subTableClassに、関連レコード一覧フィールドで自動採番されているクラス名を指定してください。

1
const subTableClass = '.value-5517967'; // 関連レコード一覧フィールドに採番されている クラス名を指定

たとえばGoogle Chromeでは、次の手順で上記のクラス名を取得します。
レコード詳細画面で開発者ツールを開き、「案件に紐付く活動履歴」のDOMを選択します。

このDOMのソースコードを見ると、クラス名はlabel-○○○○○○○となっています。

このlabel-○○○○○○○に隣接するdivのクラス名value-○○○○○○○が対象になりますので、コピーして利用してください。


以下の部分では、カードのスタイルを適用するためのクラス名を指定しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const classesToAdd = [
  {targetDom: '.container-gaia', add: 'md-container'},
  {targetDom: '#record-gaia', add: 'md-record'},
  {targetDom: '.layout-gaia', add: 'md-card-wrapper'},
  {targetDom: '.row-gaia', add: 'md-row'},
  {targetDom: '.control-gaia', add: 'md-column'},
  {targetDom: '.control-label-gaia', add: 'md-label'},
  {targetDom: '.control-value-gaia', add: 'md-value'},
  {targetDom: '.control-design-gaia', add: 'md-value--blanc'},
  {targetDom: '.control-hr-field-gaia', add: 'md-column--hr'},
  {targetDom: '.subtable-gaia', add: 'md-subtable'},
  {targetDom: '.subtable-label-gaia', add: 'md-subtable-label'},
  {targetDom: subTableClass, add: 'md-value--subtable'}
];

今回のカスタマイズでは、接頭辞md-が付与されているクラス名にスタイルが適用されます。
標準で付与されているクラス名に変更があった際は、targetDomの値を変更することで、カスタマイズが適用できるようにしています。

次に、以下の記述でカード部分のDOMを追加しています。
標準のDOM構造では、今回のカスタマイズを適用するのが難しいため、標準の<div>内に独自の<div>を追加しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// カードのスタイルを適用するために、既存の DOM を独自の DOM で囲う
document.querySelectorAll('.md-card-wrapper').forEach((wrapper) => {
  const card = document.createElement('div');
  const scrollBox = document.createElement('div');
  const scrollBoxInner = document.createElement('div');

  card.classList.add('md-card');
  scrollBox.classList.add('md-card-scroll');
  scrollBoxInner.classList.add('md-card-scroll-inner');

  // .md-card-wrapper 内にあるすべての子要素 DOM を scrollBoxInner の直下に移動
  while (wrapper.firstChild) {
    scrollBoxInner.appendChild(wrapper.firstChild);
  }

  // scrollBoxInner の DOM を scrollBox の直下に移動
  scrollBox.appendChild(scrollBoxInner);

  // scrollBox の DOM を card の直下に移動
  card.appendChild(scrollBox);

  // card の DOM を wrapper の直下に移動
  wrapper.appendChild(card);
});

出力されるhtmlは、以下のようなイメージになります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<div class="layout-gaia showlayout-gaia md-card-wrapper"> <!-- 標準のDOM -->
  <div class="md-card"> <!-- 追加したDOM 1 -->
    <div class="md-card-scroll"> <!-- 追加したDOM 2 -->
      <div class="md-card-scroll-inner"> <!-- 追加したDOM 3 -->

        <!-- レコード詳細部分のDOM -->

      </div>
    </div>
  </div>
</div>

上記のような構造にすることで、画面幅の狭いモニターでスクロールバーを出現させるCSSが指定できます。

CSSの主なポイント

カード部分には、以下のようなスタイルを指定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.md-card {
  max-width: 910px;
  margin-inline: auto;
  padding-block: 40px;
  background-color: #fff;
  border-radius: 10px;
  box-shadow: 2px 0 1px rgba(0, 0, 0, .05), /* 右方向の影 */
              -2px 0 1px rgba(0, 0, 0, .05), /* 左方向の影 */
              0 2px 1px rgba(0, 0, 0, .05), /* 下方向の影 */
              0 -2px 1px rgba(0, 0, 0, .05); /* 上方向の影 */
}

box-shadowプロパティで、要素のフレームに影を付与できます。
カンマで区切ることで、複数の値を設定できます。

たとえば、box-shadow: 2px 0 1px rgba(0, 0, 0, .05);と指定すると、以下のような影を付与できます。

  • X軸方向に2pxのオフセット
  • Y軸方向に0pxのオフセット
  • 1pxのぼかし
  • 色がrgba(0, 0, 0, .05)の影

今回のカスタマイズでは、上下左右4方向に影を付与しています。

注意事項

kintoneのクラス名は変更される可能性があります。
そのため先ほど利用したcontainer-gaiaなどの、kintoneのクラス名を使用することは推奨されていません。
詳細は kintoneで使われているidやclass属性 を確認してください。

カスタマイズの適用方法

JavaScript、CSSのサンプルコードはそれぞれ以下のような任意のファイル名をつけて保存します。

  • material-design.js
  • material-design.css

次に、「案件管理(営業支援パック)」アプリの管理画面に移動し、「設定」タブに移動します。
「カスタマイズ/サービス連携」内の「JavaScript / CSSでカスタマイズ」をクリックします。


すると、以下のような画面に移動します。
「PC用のJavaScript / CSSファイル」内の「アップロードして追加」ボタンから先ほどの2つのファイルをアップロードします。


無事、デザインが適用されました。

おわりに

この記事では、kintoneのサンプルアプリ「案件管理(営業支援パック)」をCSSでカスタマイズする方法を紹介しました。
CSSを使ってアプリの見た目を自由に変更することで、より使いやすく、視覚的に魅力的なアプリが作成できます。
ぜひ、他のアプリでも同様のカスタマイズを試してみてください。

information

このTipsは、2024年10月版kintoneで動作を確認しています。