11
3

More than 1 year has passed since last update.

stylistを使ってYewでスタイルを書く

Last updated at Posted at 2022-12-20

この記事の概要

先日初めてYewを触ってみたのですが、CSS関連はまだスタンダードが決まっていないようでした。
せっかくコンポーネントベースでWebアプリケーションが作れるので、スタイルも上手くコンポーネント内に閉じておきたいです。

ひとまず、現状では唯一のスタイリングソリューション1であるstylistを使ってスタイルを書く方法を記事にしてみました。

前提

Yewのプロジェクトがセットアップ済みだとします。

また、いくつか記法がある中でstyled_component記法は説明しません。
何故なら筆者があまり好まないからです。2

導入

Cargo.tomlstylistを追加します。
2022年12月19日現在、stylistの最新バージョンは0.11.0です(ちなみにYewは0.20.0)。
features = ["yew_integration"]も必要なのでお忘れなく。

Cargo.toml
  [dependencies]
+ stylist = { version = "0.11.0", features = ["yew_integration"] }
  yew = { version = "0.20.0", features = ["csr"] }

グローバルなスタイル

現状はこのようなコードだとします。
ここにスタイルを追加していきます。

src/main.rs
use yew::prelude::*;

#[function_component]
fn App() -> Html {
    let counter = use_state(|| 0);
    let increment = {
        // ボタン押下時の処理
    };
    let decrement = {
        // ボタン押下時の処理
    };

    html! {
        <>
            <div>
                <div>
                    <h1>{ "Counter with Yew" }</h1>
                    <span>{ *counter }</span>
                    <div>
                        <button onclick={decrement}>{ "-1" }</button>
                        <button onclick={increment}>{ "+1" }</button>
                    </div>
                </div>
            </div>
        </>
    }
}

fn main() {
    yew::Renderer::<App>::new().render();
}

まず、stylist::yew::Global;を追加します。

src/main.rs
+ use stylist::yew::Global;
  use yew::prelude::*;

グローバルに適用したいスタイルを定義します。
box-sizingとかfont-familyとか、そういうものをイメージしてください。

examplesではRaw stringの形式(r#"string"#)で書かれていましたが、軽く試した限りではLiteralsなら何でも大丈夫なようです。

src/main.rs
  #[function_component]
  fn App() -> Html {
      // 中略
+     let global_style = css!(r#"
+         * {
+             // スタイル色々
+         }
+ 
+         html {
+             // スタイル色々
+         }
+         
+         body {
+             // スタイル色々
+         }
+     "#);

その後はhtml!の中にGlobalコンポーネントを挿入すればページ全体にスタイルが適用されます。

src/main.rs
      html! {
          <>
+             <Global css={global_style} />
              <div>
                  <div>
                      <h1>{ "Counter with Yew" }</h1>
                      <span>{ *counter }</span>
                      <div>
                          <button onclick={decrement}>{ "-1" }</button>
                          <button onclick={increment}>{ "+1" }</button>
                      </div>
                  </div>
              </div>
          </>
    }

スコープの区切られたスタイル

再び、モジュールを追加します。

src/main.rs
+ use stylist::{css, style};
  use stylist::yew::Global;
  use yew::prelude::*;

定義したい分だけスタイルを追加します。

src/main.rs
  #[function_component]
  fn App() -> Html {
      // 中略
      let global_style = css!(r#"
          // 中略
      "#);
+     let container_style = style!(r#"
+         // スタイル色々
+     "#).expect("Failed to mount style");
+     let panel_style = style!(r#"
+         // スタイル色々
+     "#).expect("Failed to mount style");
+     let title_style = style!(r#"
+         // スタイル色々
+     "#).expect("Failed to mount style");
+     // 以下同様に、必要なだけスタイルを追加する

マークアップに反映させます。

src/main.rs
      html! {
          <>
              <Global css={global_style} />
-             <div>
-                 <div>
-                     <h1>{ "Counter with Yew" }</h1>
-                     <span>{ *counter }</span>
-                     <div>
+             <div class={container_style}>
+                 <div class={panel_style}>
+                     <h1 class={title_style}>{ "Counter with Yew" }</h1>
+                     <span class={counter_style}>{ *counter }</span>
+                     <div class={button_area_style}>
                          <button onclick={decrement}>{ "-1" }</button>
                          <button onclick={increment}>{ "+1" }</button>
                      </div>
                  </div>
              </div>
          </>
    }

すると、このようにハッシュ値付きのスタイルとなって生成されます。

マルチクラス

よくある使用法だと思うので、マルチクラスについても少し触れておきます。
スタイルを定義する際は変わりません。

src/main.rs
+     let button_style = style!(r#"
+         // 基本となるボタンのスタイル
+     "#).expect("Failed to mount style");
+ 
+     let button_increment_style = style!(r#"
+         background-color: #55c500; // incrementだけで使うスタイル
+     "#).expect("Failed to mount style");
+ 
+     let button_decrement_style = style!(r#"
+         background-color: #eee; // decrementだけで使うスタイル
+     "#).expect("Failed to mount style");

マークアップに追加する際は少し書式が変わります。

src/main.rs
      html! {
          <>
              <Global css={global_style} />
              <div class={container_style}>
                  <div class={panel_style}>
                      <h1 class={title_style}>{ "Counter with Yew" }</h1>
                      <span class={counter_style}>{ *counter }</span>
                      <div class={button_area_style}>
-                         <button onclick={decrement}>{ "-1" }</button>
-                         <button onclick={increment}>{ "+1" }</button>
+                         <button onclick={decrement} class={classes!(button_style.clone(), button_decrement_style)}>{ "-1" }</button>
+                         <button onclick={increment} class={classes!(button_style.clone(), button_increment_style)}>{ "+1" }</button>
                      </div>
                  </div>
              </div>
          </>
      }
  1. classes!で囲う必要がある
    • シングルクラスのときはclass={class_name}で大丈夫
    • マルチクラスの場合はclass={class_one, class_two}ではNG
      • class={classes!(class_one, class_two)}が正しい
      • シングルクラスの際にclass={classes!(class_name)}でも問題ないので、後から書き換えるのが面倒なら常にclasses!をつけておくのもアリ?
  2. 同じスタイルの場合は.clone()が必要
    • これは少し自信がない(ワークアラウンドかもしれない)
    • stylistのドキュメントの中には「こうすると良い」の記載がなかったものの、純粋に筆者がRustを分かって無さすぎるだけな可能性がある
      • 分かる方がいたらぜひコメントで教えてください!

最後に

日本語だろうが英語だろうが紹介されている記事が全然見当たらず、公式ドキュメントをずっと見ていました。
どのオプションをいつ使うと最適なのかがまだあまり分かっていないので、この記事は速攻でdeprecatedになる可能性があります。

余談ですが、公式ドキュメントの冒頭に以下の記載があります。

Add the following to your Cargo.toml:

stylist = "0.10"

しかし最新が0.11.0で、しかもBreaking Changesが入っていてドキュメント通りに書いても動きませんでした。
調べても全然出てこない中、packagesのCargo.tomlを見たらversion = "0.11.0"なのを発見。
バージョンを上げたら問題なく動いたのですが、こういうときの勘の悪さ(?)を痛感しました。


最後まで読んでくださってありがとうございます!
Twitterでも情報を発信しているので、良かったらフォローお願いします!

Devトークでのお話してくださる方も募集中です!

  1. いくつかの候補がありますが、stylist以外はあくまでコンポーネントライブラリです。https://yew.rs/docs/more/css

  2. とは言え、Reactで有名なstyled-componentsと違い、タグとスタイルが結びつきはしないのでそこまで否定的でもありません。

11
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
3