v8 開発メモ
Closed this issue · 9 comments
コンセプト
- 依存させない:コピペで部分利用可・書き捨て可・使い捨て可・プロジェクト側で追えないCSSを減らす
- 複雑にしない:内部構造の簡略化・最小限の開発環境
- フルスクラッチ
案件のコンポーネントをChakraやMUIみたいな感じで行きたいけど、納品物がSaaS用のテンプレだったりWordPressだったりするし、仮にJamstackなReact案件だとしてもアップデート追う(追ってもらう)のが非現実的なので結局は依存性のない自作に落ち着く。とはいえボタンやフォームのCSSを1から書くのはしんどい。いい感じのものを書いておいてimportではなく簡単にコピペできるようにしておきたい。見た目的にはBootstrapより今風のTailwind UIライクにして、むしろTailwind CSSと併用しても大丈夫にする。
大きな変更点
- IE非対応
- 業界全体で対応終了の流れ / 仕事でもほぼ対応していない
- CSS Variablesの出力分岐をなくせる
- gapを適応>マイナスマージンを使ったレイアウトをなくせる
- セレクタに:is()と:where()を使える
- モディファイアの再考
- 使用頻度が低く案件で書けば済むものは削除する
- パターンを増やしだすとキリがないので案件側に任せるかtailwindに任せるか考慮する
- Sass削除
- コンポーネント単位の開発で使っていない / 仕事でもまったく使っていない
- ソースコードが分かりづらくエディタで追いにくい
- v7はnode-sass準拠なのでSassを継続させるにはDart Sass準拠で作り直す必要がある
カラーパレット削除主要な色以外使っていないため
- カラーパレットはTailwind Colorから作成
- importはしないが参考用に何パターンかCSSファイルは作成しておく
- 昔のBootstrapやMaterial Designは日本のWebデザイン向けには鮮やかすぎたため、v7以前はMUSUBiiのベースとなるMOFTONEなどを自作していた。 Tailwindカラーパレットは昨今の流行りにも合うしそのまま使える色相だと感じる。
- 色名をTailwindと共用して思い出しやすくする
- 全色CSS Variablesを未圧縮で読み込んだ場合の容量は8〜9KB
- Tailwind Colorパレット作成(メモ用)
- Chakra Colorパレット作成(メモ用)
- レガシーカラーパレット作成(メモ用)
Utilitiy classのxxl~xxsは値が不明確なので1remや16pxなど固定値の名前にするデモはWeb Componentsでカプセル化した方が作りやすいかも- デモのコピペ時にCSS変数をコンパイルできると急いでる時に助かる
- デモソースはリポジトリから切り離す
- 依存アップデートbotの通知が多すぎ
- CSSテストに
HTMLReactAlpine.jsViteを採用 - CSS Variables無し版も作成(minify利用・コピペ用)PostCSS CSS Variables等
Purgecss系のドキュメントを追加する- 高位コンポーネントは別のライブラリとして作成
細かい変更点
- 全体
- 近年のデザインテイストに合わせて彩度とコントラストをUP
- primaryカラーをcyanからskyに変更
- focus効果をブラウザデフォルトにする
- hover効果の適応範囲をメディアクエリで絞り込む
- transition効果
の適応範囲をメディアクエリで絞り込むは削除 paddingの方向にinlineとblockを使用※案件側でオーバーライドしづらそうなので中止- xxl等の命名は2xl等に変更(Tailwindより)
- ブラウザによってはパフォーマンスが落ちる
text-rendering: optimizeLegibility;
を削除 - ブラウザによっては文字が見づらくなる
-webkit-font-smoothing: antialiased;
を削除
- Button
- 和欧混植フォントを指定(混ぜて書くことは稀なためどちらにも最適化しておく)
- plainの命名をsolidに変更(FontAwesomeやChakraなどよく見かける)
mildsubtleボタンを追加(Chakraより・背景はsolidよりも弱くテキストがアクセントカラー)- outlineはhoverの色変化を小さく
- meltの命名は思い出しづらいためghostに戻す
- ghostのhover背景色はそれぞれの色にする
- ボタンの高さをremで指定
- hover colorをfilterで代用
- disabled効果をopacity透過で統一
- aタグのdisabledはaria-disabled="true"推奨に変更(is-disabledも使える)
- infoを使う場面が明確でないので代わりにsecondaryを用意
- warningを使う場面はほとんどないので削除
textボタンを追加- sizeを固有Utilityとして設定
- sizeのxxsはモバイルで押しづらいので削除
- sizeのxxlは品のないデザインになりがちなので削除
- strongを削除(アンチエイリアスを削除することで文字の視認性が上がったため)
- is-width-*を追加
- is-squareとis-circleを削りis-aspect-squareを追加(Tailwindのclassに寄せた)可変でアスペクト比維持
- is-roundはis-rounded-fullに変更(Tailwindのclassに寄せた)案件でのborder-radiusパターン追加を想定した命名
- is-rounded-noneを追加
- angleにpaddingを付与(widthFull時には反映させない)
- angleのupとdownを1つのclass指定で設定できるよう変更
is-floatingはis-shadowに変更しis-shadow-coloredで色変更を分離floatingは削除
- Badge
- 和欧混植フォントを指定
- plainの命名をsolidに変更
- subtleボタンを追加
- ボタンの高さをremで指定
- デフォルトのサイズを0.75remに変更
- sizeを固有Utilityとして設定
- size変更の値が小さく余白に端数がでがちなのでpaddingは固定値を使用
- strongを削除
- is-squareとis-circleを削りis-aspect-squareを追加
- is-roundはis-rounded-fullに変更
- アイコンのみの使用を想定しない
開発ディレクトリ
Demo with react
試しにブラウザ用Reactで作ったデモ。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Button</title>
<link rel="stylesheet" href="../../css/musubii.css" />
<link rel="stylesheet" href="../demo.css" />
<script
crossorigin
src="https://unpkg.com/react@17/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script
data-plugins="transform-es2015-modules-umd"
type="text/babel"
src="../demo.js"
></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel" data-plugins="transform-es2015-modules-umd">
import { DemoRadios } from "../demo.js"
const App = () => {
const variants = ["plain", "outline", "ghost"]
const [variant, setVariant] = React.useState("plain")
return (
<div className="demo-contents">
<div className="demo-content">
<DemoRadios
items={variants}
name="variant"
checked={variant}
action={(value) => setVariant(value)}
/>
</div>
<div className="demo-content">
<Buttons variant={variant} />
</div>
</div>
)
}
const Buttons = ({ variant }) => {
return (
<div className="demo-buttons">
<Button variant={variant} text="戻る" />
<Button variant={variant} text="決定" color="primary" />
<Button variant={variant} text="変更" color="secondary" />
<Button variant={variant} text="登録" color="success" />
<Button variant={variant} text="削除" color="danger" />
</div>
)
}
const Button = ({ variant, text, color }) => {
const classNames = [
"button",
variant ? `is-${variant}` : "is-plain",
color && `is-${color}`,
].join(" ")
return (
<button type="button" className={classNames}>
{text}
</button>
)
}
const app = document.querySelector("#app")
const element = <App />
ReactDOM.render(element, app)
</script>
</body>
</html>
const DemoRadios = ({ items, name, checked, action }) => {
return (
<div className="demo-radios">
{items.map((item, index) => (
<label className="demo-radio" onClick={() => action(item)} key={index}>
<input
type="radio"
name={name}
onChange={() => action(item)}
checked={checked === item}
/>
<span>{item}</span>
</label>
))}
</div>
)
}
export { DemoRadios }
Demo with Alpine.js
ReactDOMのレンダリングがライブリロードと相性良くなかったのでAlpine.jsに変更。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Button</title>
<link rel="stylesheet" href="../../css/musubii.css" />
<link rel="stylesheet" href="../demo.css" />
<script src="https://unpkg.com/alpinejs" defer></script>
</head>
<body>
<div class="demo-contents" x-data="{ variant: 'plain' }">
<div class="demo-content">
<div class="demo-radios">
<label class="demo-radio">
<input
type="radio"
name="variant"
x-model="variant"
value="plain"
/>
<span>plain</span>
</label>
<label class="demo-radio">
<input
type="radio"
name="variant"
x-model="variant"
value="outline"
/>
<span>outline</span>
</label>
<label class="demo-radio">
<input
type="radio"
name="variant"
x-model="variant"
value="ghost"
/>
<span>ghost</span>
</label>
</div>
</div>
<div class="demo-content">
<div class="demo-buttons">
<button class="button" :class="'is-'+variant" type="button">
戻る
</button>
<button
class="button is-primary"
:class="'is-'+variant"
type="button"
>
決定
</button>
<button
class="button is-secondary"
:class="'is-'+variant"
type="button"
>
変更
</button>
<button
class="button is-success"
:class="'is-'+variant"
type="button"
>
登録
</button>
<button class="button is-danger" :class="'is-'+variant" type="button">
削除
</button>
</div>
</div>
</div>
</body>
</html>
alpine.jsのx-bindが複数classで効かない(previewプラグインのバグかも)のと、ホットリロードでStateを保持しながらプレビューしたくなったのでVite+Reactでデモを作成する。
React Componentsの作業を楽にしたかったので、TypeScriptで書き直した。
Viteからのtsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["./src", "./test"]
}
floating・shadowのパターンは複雑になりすぎるため、プロジェクト側でTailwindなどを使う方が良さそう。shadow colordはCSS Variable無しでは実装できないので、無しバージョンを作る今回のアップデートに向かないというのもある。
初期のメモ
コピペで部分利用できるようにする | 内部構造の簡略化 |
---|---|
アプデのきっかけ
全部入りimportによる下地作りは、コンポーネント単位の開発で使いにくいかもと思った。
v7リリース後の1年(2021年)にMUSUBiiを参照したのは主にボタンとフォーム。案件でMUSUBiiのボタンangleスタイルやselectのスタイルを使おうとしたものの、そのためだけにimportで依存を増やしたくなかった。importして納品してもその後のアップデートは追えないだろうし、class名カスタマイズなどの機能を使った場合はドキュメントもあまり役に立たなくなる。スタイルだけコピペして依存なく使えると便利。
また、Sassを用いた構造が難解で改修しづらいため簡略化する。
このコンセプトの開発は別のOSSに引き継ぐためクローズします。