ホームページ > ウェブフロントエンド > CSSチュートリアル > Tailwind CSS を使用してコンポーネント バリアントを作成するための別のアプローチ

Tailwind CSS を使用してコンポーネント バリアントを作成するための別のアプローチ

Patricia Arquette
リリース: 2024-11-21 05:35:11
オリジナル
661 人が閲覧しました

A different approach to writing component variants with Tailwind CSS

問題

Tailwind CSS でコンポーネント バリアントを作成する場合、従来、prop 値をコンポーネント スロットにマップする単純なクラス マップを使用していました。

type TTheme = "DEFAULT" | "SECONDARY";
interface IComponentSlot {
  root: string;
  accent: string;
}

const THEME_MAP: Record<TTheme, IComponentSlot> = {
  DEFAULT: {
    root: "bg-red hover:background-pink",
    accent: "text-blue hover:text-green",
  },
  SECONDARY: {
    root: "bg-green hover:background-black",
    accent: "text-pink hover:text-white",
  }
}

<div :class="THEME_MAP['DEFAULT'].root">
  <div :class="THEME_MAP['DEFAULT'].accent">/**/</div>
</div>
ログイン後にコピー

このアプローチの問題は、特により複雑なコンポーネントにおいて、必要なクラスを相互に整合させ、各バリアントに必要なクラスがすべて確実に含まれるようにすることです。異なるコンポーネント スロット間でスタイル (テキストの色など) を共有したいコンポーネントでは、各スロットを個別に更新する必要があります。

Tailwind の制限

Tailwind は、コードベースをスキャンして文字列を照合することによってユーティリティ クラスを生成します。つまり、Tailwind は任意の値からクラスを作成できますが、セーフ リストを作成せずにクラスを動的に開始することはできません。したがって、これは機能しません:

// .ts
type TTheme = "DEFAULT" | "SECONDARY";

const colors: Record<TTheme, string> = {
  DEFAULT: "red",
  SECONDARY: "blue",
}

// .html
<div :class="`text-[${colors[DEFAULT]}]`">
ログイン後にコピー

ただし、CSS 変数を利用することで、目的の動作を模倣することができます。CSS 変数は、Tailwind が多くのクラスに対して内部で使用しているものです。次の構文を使用して、Tailwind のクラス経由で変数を設定できます: [--my-variable-key:--my-variable-value]
それでは、動的値を使用するように上記のコード例を更新するにはどうすればよいでしょうか?

// .ts
type TTheme = "DEFAULT" | "SECONDARY";

const colors: Record<TTheme, string> = {
  DEFAULT: "[--text-color:red]",
  SECONDARY: "[--text-color:blue]",
}

// .html
<div
  :class="[
    colors[DEFAULT],
    'text-[--text-color]'
  ]">
ログイン後にコピー

最初の問題に取り組む

Tailwind の制限を理解したので、クラス マップ アプローチによって引き起こされた最初の問題を解決する方法を検討する必要があります。まず、クラス マップを単純化することから始めます。

type TTheme = "DEFAULT" | "SECONDARY";
interface IComponentSlot {
  root: string;
  accent: string;
}

const THEME_MAP: Record<TTheme, string> = {
  DEFAULT: "[--backgound:red] [--hover__background:pink] [--text:blue] [--hover__text:green]",
  SECONDARY: "[--backgound:green] [--hover__background:black] [--text:pink] [--hover__text:white]",
}

<div>



<p>Unfortunately, this alone doesn't solve our problem, we still can't ensure we've set all of the classes we need to display each variant correctly. So how can we take this a step further? Well, we could begin writing an interface to force us to set specified values:<br>
</p>

<pre class="brush:php;toolbar:false">interface IComponentThemeVariables {
  backgound: string;
  hover__backgound: string;
  text: string;
  hover__text: string;
}

const THEME_MAP: Record<TTheme, IComponentThemeVariables> = {
  DEFAULT: {
    backgound: "[--backgound:red]",
    text: "[--hover__background:pink]",
    hover__background: "[--text:blue]",
    hover__text:"[--hover__text:green]",
  },
  SECONDARY: {
    backgound: "[--backgound:green]",
    text: "[--hover__background:black]",
    hover__background: "[--text:pink]",
    hover__text:"[--hover__text:white]",
  },
}
ログイン後にコピー

これはうまくいきますが、まだ問題があり、文字列値を混同することを妨げるものは何もありません。たとえば、キーの背景を誤って [--text:blue] に設定してしまう可能性があります。

それでは、値も入力する必要があるかもしれません。クラス全体を入力することはできません。それはメンテナンスの悪夢になります。そのため、色を入力し、CSS 変数を生成するヘルパー メソッドを書いたらどうでしょうか。

type TColor = "red" | "pink" | "blue" | "green" | "black" | "white";

interface IComponentThemeVariables {
  backgound: TColor;
  hover__backgound: TColor;
  text: TColor;
  hover__text: TColor;
}

// Example variableMap method at the end of the article

const THEME_MAP: Record<TTheme, string> = {
  DEFAULT: variableMap({
    backgound: "red",
    text: "pink",
    hover__background: "blue",
    hover__text:"green",
  }),
  SECONDARY: variableMap({
    backgound: "green",
    text: "black",
    hover__background: "pink",
    hover__text:"white",
  }),
}
ログイン後にコピー

わかりました。これは素晴らしいことです。コンポーネントのすべてのバリアントに対して常に正しい変数を設定していることを確認できます。しかし、待ってください。Tailwind で発見した最初の問題に遭遇したところです。単にクラスを生成することはできず、Tailwind はクラスを取得しません。では、これにどう対処するのでしょうか?

JS の CSS についてはどうですか?

JS 内の CSS は、ここでは明白な答えのように思えます。正しい変数を使用してカスタム クラスを作成するクラスを生成するだけです。しかし、問題があり、Javascript がクライアント上で実行され、これにより「フラッシュ」が発生します。この場合、コンポーネントは、正しく表示するために更新される前に、変数が設定されていない状態で最初に読み込まれます。

JS ライブラリの CSS はこれにどのように対処しますか?

Emotion のようなライブラリは、コンポーネントに関するインライン スタイル タグを挿入することでこれに対処します。

<body>
  <div>
    <style data-emotion-css="21cs4">.css-21cs4 { font-size: 12 }</style>
    <div>



<p>This didn't feel like the right approach to me.</p>

<h3>
  
  
  So how do we solve this?
</h3>

<p>I was working with Vue, this led me down the path of v-bind in CSS, a feature in Vue to bind Javascript as CSS values. I'd only used this feature sparingly in the past and never taken a deep dive into what it's doing. v-bind in CSS simply sets an inline style on the relevant element.</p>

<p>This jogged my memory about a Tweet I saw from the creator of Tailwind CSS, Adam Wathan a couple of months previously:</p>

<p>So how does this help us? Well, while we can't dynamically generate Tailwind classes, we can dynamically generate inline styles and consume those inline styles from our Tailwind classes. So what would that look like?<br>
</p>

<pre class="brush:php;toolbar:false">type TColor = "red" | "pink" | "blue" | "green" | "black" | "white";

interface IComponentThemeVariables {
  backgound: TColor;
  hover__backgound: TColor;
  text: TColor;
  hover__text: TColor;
}

// Example variableMap method at the end of the article

const THEME_MAP: Record<TTheme, string> = {
  DEFAULT: variableMap({
    backgound: "red",
    text: "pink",
    hover__background: "blue",
    hover__text: "green",
  }),
  SECONDARY: variableMap({
    backgound: "green",
    text: "black",
    hover__background: "pink",
    hover__text: "white",
  }),
}

<div
 >



<h2>
  
  
  Conclusion
</h2>

<p>By combining the powers of Typescript, CSS variables, and inline styles we were able to ensure that while using Tailwind CSS, each variant of our component would have every option set and with the correct type.</p>

<p>This is an experimental approach on which I'm sure there will be some strong opinions. Am I convinced this is the best approach? At this stage, I'm not sure, but I think it has legs.</p>

<p>If you've found this article interesting or useful, please follow me on Bluesky (I'm most active here), Medium, Dev and/ or Twitter.</p>

<h3>
  
  
  Example: variableMap
</h3>



<pre class="brush:php;toolbar:false">// variableMap example
export const variableMap = <T extends Record<string, string>>(
  map: T
): string => {
  const styles: string[] = [];
  Object.entries(map).forEach(([key, value]) => {
    const wrappedValue = value.startsWith("--") ? `var(${value})` : value;
    const variableClass = `--${key}: ${wrappedValue};`;
    styles.push(variableClass);
  });
  return styles.join(" ");
};
ログイン後にコピー

以上がTailwind CSS を使用してコンポーネント バリアントを作成するための別のアプローチの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート