EY-Office ブログ

ReactでBulma CSSを直接使うのがお気に入り

最近Reactのサンプルコードを作るのにBulmaというCSS Frameworkを使っています。私も多くのWebプログラマーと同じく、まったくWebページのデザインはできません😅

いままで仕事で作ったサイトは、お客様の会社の方またはお客様と取引のあるデザイン会社の方にデザインしてもらっていました。(Photoshop/Illustrator等で作られたデザイン画からHTML/CSSに変換するのは出来ます)

Bulma

Bootstrap

2011年、そんなプログラマーに嬉しいプロダクトが公開されました Bootstrap です。Twitter社のWebデザイナーが公開したCSSフレームワークでルールに沿ってHTML/CSSを書けば、レスポンシブルでクールなサイトができます!

世界中のWebプログラマーに使用され、またたく間に広まりました。

Material-UI

Reactを使うようになり、Bootstrapの替わりMaterial Designを実装したMaterial-UIを使うようになりました。Material-UIはHTML/CSSではなく<Button>などのReactコンポーネントととして提供されています。
素晴らしいコンポーネントですが、実際のアプリや教材としても使っていて2つの問題を感じました。

1. 何を書くにも通常のHTMLではなくMaterial-UIコンポーネントを使わないといけない

テーブルを表示するには <Table><TableBody><TableRow><TableCell>...</TableCell></TableRow></TableBody></Table> のように独自のコンポーネントに置き換える必要がある。

慣れればたいした問題ではありませんが、教育では初心者への負担が増えるので困ったことです。タグ名はHTMLに似ていますが、属性指定はかなり違います。

2. ちょっとしたレイアウトや色の変更がめんどう

Material-UIコンポーネントは、とくに指定しなくてもそれなりに配置されますが、隣のコンポーネントと少し離したい、色を少し変えたい・・・ などが面倒です。

色などはコンポーネントの属性で変えられますが、隣のコンポーネントと少し離したい場合はstyle={{marginLeft: 10}}のようにstyle属性で指定する必要があり面倒です。

そこでBulma

まず見た目ですが、CSS Frameworkを使わないアプリが

Bulmaを使うと、こんな素敵なアプリに生まれ変わります!

BlumaにもReactコンポーネント化した React-bulma-components がありますが、Blumaでは通常のHTMLにCSS指定するだけで使えます。

コードを見てみましょう

以下のコードを眺めるとわかるように、 通常のHTMLにBulmaのCSS指定しているだけなので判りやすいです。style=で指定するよりコードも短くなっていたりします。

またSpacing用のユーティリティークラス ml-4 (margin-left: 1rem; の意味)px-5 (padding-left: 1.5rem; padding-right: 1.5rem; の意味) があり配置も楽です。
has-background-info-lightのようなバックグラウンドの色指定ユーティリティークラスも便利ですね。

今後教材のコードでは Bulma を使って行こうと思います。😘


  • Jyankenコンポーネント
const Jyanken: React.FC = () => {

  ・・・

   return (
    <>
      <h1>じゃんけん ポン!</h1>
      <JyankenBox actionPon={te => pon(te)} />
      <ScoreBox scores={scores} />
    </>
   )
}

    ↓ Bulma

import 'bulma/css/bulma.css'
const Jyanken: React.FC = () => {

  ・・・

  return (
    <div className="section">
      <h1 className="title">じゃんけん ポン!</h1>
      <JyankenBox actionPon={te => pon(te)} />
      <ScoreBox scores={scores} />
    </div>
   )
}
  • JyankenBoxコンポーネント
const JyankenBox: React.FC<JyankenBoxProps> = ({actionPon}) => {
  const divStyle: React.CSSProperties = {margin: "0 20px"}
  const buttonStyle: React.CSSProperties = {margin: "0 10px",padding: "3px 10px",
    fontSize: 14}
  return (
    <div style={divStyle}>
      <button onClick={() => actionPon(Te.Guu)} style={buttonStyle}>グー</button>
      <button onClick={() => actionPon(Te.Choki)} style={buttonStyle}>チョキ</button>
      <button onClick={() => actionPon(Te.Paa)} style={buttonStyle}>パー</button>
    </div>
  )
}

   ↓ Bulma

const JyankenBox: React.FC<JyankenBoxProps> = ({actionPon}) => {
  return (
    <div className="buttons ml-4">
      <button onClick={() => actionPon(Te.Guu)} className="button is-primary">グー</button>
      <button onClick={() => actionPon(Te.Choki)} className="button is-primary">チョキ</button>
      <button onClick={() => actionPon(Te.Paa)} className="button is-primary">パー</button>
    </div>
  )
}
  • ScoreBoxコンポーネント
const ScoreBox: React.FC<ScoreListProps> = ({scores}) => {
  const teString = ["グー","チョキ", "パー"]
  const judgmentString = ["引き分け","勝ち", "負け"]

  const tableStyle: React.CSSProperties = {marginTop: 20, borderCollapse: "collapse"}
  const thStyle: React.CSSProperties = {border: "solid 1px #888", padding: "3px 15px"}
  const tdStyle: React.CSSProperties = {border: "solid 1px #888", padding: "3px 15px",
      textAlign: "center"}
  return (
    <table style={tableStyle}>
      <thead>
        <tr>
          <th style={thStyle}>あなた</th>
          <th style={thStyle}>コンピュター</th>
          <th style={thStyle}>勝敗</th>
        </tr>
      </thead>
      <tbody>
        {scores.map((scrore, ix) =>
          <tr key={ix}>
            <td style={tdStyle}>{teString[scrore.human]}</td>
            <td style={tdStyle}>{teString[scrore.computer]}</td>
            <td style={tdStyle}>{judgmentString[scrore.judgment]}</td>
          </tr>
        )}
      </tbody>
    </table>
  )
}

    ↓ Bulma

const ScoreBox: React.FC<ScoreListProps> = ({scores}) => {
  const teString = ["グー","チョキ", "パー"]
  const judgmentString = ["引き分け","勝ち", "負け"]

  return (
    <table className="table is-bordered is-striped">
      <thead>
        <tr className="has-background-info-light">
          <th className="px-5">あなた</th>
          <th className="px-5">コンピュター</th>
          <th className="px-5">勝敗</th>
        </tr>
      </thead>
      <tbody>
        {scores.map((scrore, ix) =>
          <tr key={ix}>
            <td>{teString[scrore.human]}</td>
            <td>{teString[scrore.computer]}</td>
            <td>{judgmentString[scrore.judgment]}</td>
          </tr>
        )}
      </tbody>
    </table>
  )
}

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ