qrac / musubii

Simple CSS Framework for JP
https://musubii.qranoko.jp
150 stars 6 forks source link

v8 開発メモ #293

Closed qrac closed 2 years ago

qrac commented 2 years ago

コンセプト

案件のコンポーネントをChakraMUIみたいな感じで行きたいけど、納品物がSaaS用のテンプレだったりWordPressだったりするし、仮にJamstackなReact案件だとしてもアップデート追う(追ってもらう)のが非現実的なので結局は依存性のない自作に落ち着く。とはいえボタンやフォームのCSSを1から書くのはしんどい。いい感じのものを書いておいてimportではなく簡単にコピペできるようにしておきたい。見た目的にはBootstrapより今風のTailwind UIライクにして、むしろTailwind CSSと併用しても大丈夫にする。

大きな変更点

細かい変更点

開発ディレクトリ

qrac commented 2 years ago
buttons-wip-21122101

ボタンはinfoの使い所が不明確なのでsecondaryに変更し、同じく使い所のなかったwarningを削除。全体的に彩度とコントラストを調整。

qrac commented 2 years ago

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 }
qrac commented 2 years ago

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>
qrac commented 2 years ago

alpine.jsのx-bindが複数classで効かない(previewプラグインのバグかも)のと、ホットリロードでStateを保持しながらプレビューしたくなったのでVite+Reactでデモを作成する。

qrac commented 2 years ago

React Componentsの作業を楽にしたかったので、TypeScriptで書き直した。

qrac commented 2 years ago

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"]
}
qrac commented 2 years ago

floating・shadowのパターンは複雑になりすぎるため、プロジェクト側でTailwindなどを使う方が良さそう。shadow colordはCSS Variable無しでは実装できないので、無しバージョンを作る今回のアップデートに向かないというのもある。

qrac commented 2 years ago

初期のメモ

コピペで部分利用できるようにする 内部構造の簡略化
image image

アプデのきっかけ

全部入りimportによる下地作りは、コンポーネント単位の開発で使いにくいかもと思った。

v7リリース後の1年(2021年)にMUSUBiiを参照したのは主にボタンとフォーム。案件でMUSUBiiのボタンangleスタイルやselectのスタイルを使おうとしたものの、そのためだけにimportで依存を増やしたくなかった。importして納品してもその後のアップデートは追えないだろうし、class名カスタマイズなどの機能を使った場合はドキュメントもあまり役に立たなくなる。スタイルだけコピペして依存なく使えると便利。

また、Sassを用いた構造が難解で改修しづらいため簡略化する。

qrac commented 2 years ago

このコンセプトの開発は別のOSSに引き継ぐためクローズします。