本文へ移動
uiify

スタイリング

コンポーネントはセマンティックなマークアップと文書化された状態の契約を提供し、見た目のスタイルは含みません。通常のCSS、Tailwind、任意の@gunkin/uiify/stylesレイヤーでスタイルを指定できます。

スタイリングの契約

すべての公開パーツは、className、style、ref、イベントハンドラー、ネイティブprops、data-*属性を直接受け取ります。contentClassNameやtriggerClassNameのような並行するpropはありません。

識別属性と状態属性はuiifyが管理します。これらはスタイリングのフックです。状態を変更するときはpropsかHTMLのネイティブな動作を使い、data-stateを直接書き換えないでください。--uiify-*名前空間は、寸法や配置、アニメーション、インスタンスごとの値のために予約されています。

<Popover.Root>
  <Popover.Trigger className="toolbar-trigger">Open</Popover.Trigger>
  <Popover.Content className="toolbar-popover" data-layout="compact">
    Actions
  </Popover.Content>
</Popover.Root>

命名

フック用途例
識別マーカーuiifyが管理する、安定したコンポーネントの識別子data-uiify-popover
構造マーカーuiifyが管理する、公開された複合パーツdata-part="content"
状態属性セレクターが参照する、読み取り専用の状態の写しdata-state="open"
カスタムプロパティ動的な寸法や配置、インスタンスごとの値--uiify-anchor-x

状態を持つ各ルートには、存在自体を値とするdata-uiify-<component>マーカーが1つあります。各複合パーツには、trigger、content、item、labelなど、ケバブケースのdata-part値が1つあります。利用側のpropsでこれらの値は上書きできません。

通常のCSSとTailwind

[data-uiify-popover][data-part="content"] {
  min-inline-size: 12rem;
}

[data-uiify-popover][data-part="content"][data-state="open"] {
  opacity: 1;
}

/* Tailwind uses the same attributes. */
<div className="data-[state=open]:opacity-100" />

ポリモーフィズム

asとrenderはレンダリングする要素を置き換えます。カスタムレンダラーは、受け取ったpropsとrefをDOM要素に転送する必要があります。転送しないと、マーカー、状態、スタイリングが機能しません。

behavior.cssは必須

このスタイルシートには、状態を動かすためのルールが含まれます。ポップオーバーの配置、::details-contentのサイズ調整、:has()による伝播、非表示入力の配置、@starting-styleのトランジションです。色、フォント、余白は含みません。

import "@gunkin/uiify/components/behavior.css";

読み込まなければ、コンポーネントは描画されても状態が機能しません。アプリ内の任意の場所で一度インポートしてください。

状態を表す属性

コンポーネントは、状態を持つ要素のdata-*属性として状態を公開します。CSSはJavaScriptやクラス名なしで状態を選択できます。各コンポーネントページに、出力される属性を記載しています。

.my-trigger[data-state="open"] { … }
.my-item[data-disabled] { … }

/* Native state works the same way, because the state IS native */
.my-panel:popover-open { … }
details[open] > summary { … }

バリエーションもdata-*属性で表し、コンテナースタイルクエリーは使いません。コンテナーは自分自身をクエリーできないためです。

値はインライン、ルールはスタイルシート

インスタンスごとの値はインラインのカスタムプロパティで渡し、すべてのルールは静的なスタイルシートに記述します。インラインスタイルでは、状態モデルが依存する擬似クラス、擬似要素、コンテナークエリー、@starting-styleを表現できません。

インラインのプロパティはハイドレーションの不一致を起こさずHTMLとともにストリーミングされ、1つのスタイルシートは一度だけ解析されてキャッシュされます。CSS-in-JSは境界ごとに<style>の断片を挿入し、リクエスト間でキャッシュされません。

スタイリングの3つの方法

独自のCSS。 要素とそのdata-*属性を選択します。上書きが必要な既定のスタイルはありません。

Tailwind。 同じ属性をdata-[state=open]:rotate-180のようなバリエーションとして使えます。サイトのプレビューは、ヘッダーのスタイル設定からTailwindスキンに切り替えられます。

@gunkin/uiify/styles。 OKLCHのトークンスケール、ライト・ダークテーマ、全コンポーネントのスキンを提供する、任意の通常CSSレイヤーです。出発点として使えますが、必須ではありません。

import "@gunkin/uiify/styles/reset";
import "@gunkin/uiify/styles";

カスケードの順序

uiifyが提供するものはすべて、利用側より下位の名前付きレイヤーに配置されます。

@layer ui.reset, ui.behavior, ui.tokens, ui.theme, app;

利用側のレイヤーが常に優先されます。上書きに!importantや詳細度の工夫は不要で、ライブラリをリファクタリングしても維持されます。

各コンポーネントページには、属性とCSS変数に加え、両スキン用のコピーして使えるCSSを記載しています。