LPやWeb製作を高速化するUIコンポーネント集を作った
リンク
コンポーネントの例
概要とモチベーション
Camome UI はひとことで言うと「抽象度の高い UI コンポーネント集」です。
Bootstrap や MUI などのフレームワーク・ライブラリの類とは異なり、網羅的・汎用的なパーツを提供することが目的ではありません。つまり Button
や Input
や、もう少しハイレベルな Navbar
などを高いカスタマイズ性とともに提供するつもりはありません。
それよりも「LP のこの部分にコピペすればサクッと決まる」ような体験を重視しています。UI デザインといっても、多くの場合はそれほど変わったものが求められるわけではなく、使い回せるものが大半だと思います。
要するに「デザインの常套句」みたいなものが存在するわけです。Camome UI はそういう「よくある」デザインをたくさん収録することを目的としています。
プログラミングは得意でもデザインに回す余力がない人などに使ってもらえたらなと思います。
特徴
シンプルで使いやすいデザイン
当たり前ですが、あまり特徴的な見た目だと用途が狭くなるので、できるだけシンプルにしてあります。また、あまりカッコつけすぎず、古臭すぎないちょうどいいポイントを狙っています。
フレームワーク依存なし
生の HTML + CSS で提供しており、他のライブラリやバンドラーなどには依存していません。
ただ開発も HTML と CSS の生打ちだとツラすぎるので、React と CSS Modules でコンポジションを行い、Next.js で静的アセットとして出力しています。(後述)
JavaScript なし
今後変わるかもしれませんが、現状は JavaScript を使用していません。ただそこにポリシーがあるわけではなく、React コードから Vanilla JS をいい感じに出力する方法がパッと思いつかないだけです・・・。
それよりは React コンポーネントライブラリとして提供したほうが楽そうですが、CSS Modules をいい感じにバンドルするのもまた面倒くさそうです・・・。
日本語でも違和感が少ない
大半の UI ライブラリやテンプレートなどは欧米言語向けなので(Ant Designは中国語もサポートしている思いますが)、そのまま日本語を入力するとバランスが悪くなったりすることが多い気がします(例えば見出しがやたらとデカくなったりします)。
Camome UI は最初から日本語を前提に開発されているので、少なくとも開発者(僕)の意図と異なったバランスにはなりません。
使いかた
グローバルな CSS ファイル(リセット、デザイントークンなどを含む)に加えて、任意のコンポーネントの HTML + CSS ファイルをプロダクトに導入するだけの単純な構成です。
以下のサンプルコードのように BEM風のクラス名がつけられているので、コンポーネント間で干渉することはない(はず)です。
サンプル HTML
<section class="stats1">
<div class="stats1__inner">
<div class="stats1__heading">
<h2>説得力のあるデータがあります</h2>
<p class="stats1__heading__sub">
それといっしょにまっすぐに歩いていたのでした。ほんとうに幸になるなら、どんなことでもそれがはっきりしませんでした。
</p>
</div>
<dl class="stats1__dataList">
<div class="stats1__dataList__item">
<dt>顧客満足度</dt>
<dd>98%</dd>
</div>
<div class="stats1__dataList__item">
<dt>同業者比コスト</dt>
<dd>-18%</dd>
</div>
<div class="stats1__dataList__item">
<dt>取引実績</dt>
<dd>120社</dd>
</div>
</dl>
</div>
</section>
サンプル CSS
.stats1 {
background-color: var(--camome-colors-accent-900);
width: 100%;
}
.stats1__inner {
width: 100%;
max-width: var(--camome-screen-xl);
margin: 0 auto;
padding: var(--camome-space-16) var(--camome-space-4);
display: grid;
grid-gap: var(--camome-space-6);
gap: var(--camome-space-6);
}
.stats1__heading {
display: grid;
grid-gap: var(--camome-space-3);
gap: var(--camome-space-3);
text-align: center;
}
.stats1__heading h2 {
font-size: var(--camome-fontSizes-3xl);
font-weight: var(--camome-fontWeights-bold);
color: var(--camome-colors-white);
}
.stats1__heading__sub {
color: var(--camome-colors-gray-300);
}
.stats1__dataList {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: var(--camome-space-8);
gap: var(--camome-space-8);
}
.stats1__dataList__item {
display: flex;
flex-direction: column-reverse;
align-items: center;
justify-content: center;
gap: var(--camome-space-0);
}
.stats1__dataList__item dd {
font-size: var(--camome-fontSizes-6xl);
font-weight: var(--camome-fontWeights-bold);
color: var(--camome-colors-white);
}
@media screen and (max-width: 768px) {
.stats1__dataList__item dd {
font-size: var(--camome-fontSizes-4xl);
}
}
@media screen and (max-width: 576px) {
.stats1__dataList__item dd {
font-size: var(--camome-fontSizes-6xl);
}
}
.stats1__dataList__item dt {
font-size: var(--camome-fontSizes-lg);
color: var(--camome-colors-gray-300);
}
@media screen and (max-width: 768px) {
.stats1__dataList__item dt {
font-size: var(--camome-fontSizes-md);
}
}
@media screen and (max-width: 576px) {
.stats1__dataList__item dt {
font-size: var(--camome-fontSizes-lg);
}
.stats1__dataList {
grid-template-columns: repeat(1, 1fr);
}
}
Web 標準しか使用していないため、React などの JS フレームワークに導入することもそれほど面倒ではないと思います。
詳細は上記のドキュメントを参照してください。
どうやって開発したか
Camome UI の特徴を紹介してきましたが、ここからはどうやってこのコンポーネント集を開発しているかを説明します。
Next.js + CSS Modules --> HTML + CSS を生成
HTML と CSS を生で書き続けるのは大変です。例えば Button
はいろんなコンポーネントで使い回されているので、少しの変更でも影響する全ファイルを書き直す必要があります。
Camome UI では React を CSS Modules でスタイリングすることでそういった煩雑さを解消しています。
CSS Modules によるスコープの付与
css-loader
の設定によって、クラス名の頭にコンポーネントのファイル名を付加することで、HTML + CSS に書き出した際にもコンポーネントごとにスコープを閉じることができます。
.container {
padding: 2rem;
}
.heading {
font-weight: bold;
}
/* ファイル名のprefixでスコープが閉じられる
<div class="Card__container">
<h2 class="Card__heading">見出し</h2>
</div>
*/
Next.js による静的レンダリング
公開するコンポーネントは Next.js の pages/
ディレクトリに配置しています。
これによって next export
コマンドで静的アセット(HTML + CSS + JS)としてレンダリングされるので、あとは各ページの HTML ファイルから参照している CSS ファイルを収集するだけです。HTML から余計な head
要素などもクレンジングする必要もあります。
こういった作業にはjsdomを使いました。また、書き出された HTML は微妙に崩れているので、Prettier の JS API でフォーマットしてあります。
プレビュー
Next.js のページに置いてあるコンポーネントは、当たり前ですが本来の目的どおり Web ページとして閲覧できます。
これをドキュメント内のプレビュー画面としても利用しています。 preview.camome.net
というサブドメイン上で配信しており、 camome.net
のドキュメントに iframe
で埋め込んであります。
ドキュメント
ドキュメント(ドキュメンテーションズ?)はDocusaurusを使っています。
UI コンポーネント集としてはもっと良い表示方法があるとは思うし、カスタマイズするなら Docusaurus じゃなくても良い気もしますが、少なくともここまで作成するのはかなり楽でした。より文字情報主体の docs を構築するなら文句なくオススメだと思います。
HTML と CSS コードを含む各コンポーネントのページは、Next.js で静的に出力されたソースを元に自動生成しています。つまり Markdown ファイルは git に含まれておらず、ビルド時に毎回生成されます。
pnpm Workspace によるモノリポ
- Next.js による静的ファイル出力 + プレビュー
- Docusaurus によるドキュメントサイト
この 2 つのアプリケーションは、pnpm Workspaceを利用したモノリポで管理しています。
以下のような yaml ファイルをプロジェクトルートに追加するだけでモノリポ化でき、いまのところはかなり快適な開発体験を得られています。
packages:
- apps/*
Vercel のモノリポ対応もかなり進んでいるようなので、基本的にはルートディレクトリを指定するだけで適切にビルドしてくれます。
まとめ
コンポーネントは今後も追加していく予定です。現在は LP で使えるパーツがメインですが、もっと Web アプリに使えそうなものも増やしていこうと思っています。
コンポーネントの追加などの情報はこちらのメーリングリストで配信予定です。
ドキュメントとデモサイトは以下のリンクから ↓
Discussion