スタイルを指定する方法は、適用する先によって異なります。要素の Shadow DOM の中には ctx.css でスタイルを適用し、ページにはサーバーコンポーネントが import した CSS を適用します。ページの CSS は要素の中に届かず、要素の CSS はページに影響しません。
要素のスタイル
要素の関数は、2 番目の引数で受け取る ctx の css で、スタイルを指定します。
// src/elements/user-card.tsx
import type { Ctx } from "sobria";
export default function UserCard(props: { name: string }, { css }: Ctx) {
css`
:host {
display: block;
}
.name {
font-weight: bold;
}
`;
return <p class="name">{props.name}</p>;
}css は、要素の関数の本体で呼び出します。複数回呼び出すと、呼び出した順に適用します。サーバーで描画した HTML にもスタイルが含まれるので、要素の JS が届く前から同じ見た目で表示されます。
css のタグ付きテンプレートに埋め込んだ値は、css を呼び出した時点の値に固定されます。状態によって見た目を変更するときは、class や style の属性を使用します。
CSS ファイルを要素に読み込む
CSS ファイルを with { type: "css" } を付けて import すると、CSSStyleSheet として受け取れます。このシートを css に渡します。
/* src/styles/card.css */
.card {
border: 1px solid #ccc;
padding: 8px;
}// src/elements/card.tsx
import type { Ctx } from "sobria";
import sheet from "../styles/card.css" with { type: "css" };
export default function Card(props: { heading: string }, { css }: Ctx) {
css(sheet);
return <h2 class="card">{props.heading}</h2>;
}同じ CSS ファイルを複数の要素で import すると、すべての要素が同じシートを共有します。
css に渡せるシートは、with { type: "css" } で import したものだけです。new CSSStyleSheet() で作成したシートを渡すと、サーバーで描画するときにエラーになります。動的な import() で type: "css" を指定すると、ビルドのエラーになります。
複数の要素でスタイルを共有する
Tailwind CSS のように、ページ全体で使用する 1 つの CSS を生成するツールがあります。生成した CSS をページに読み込んでも、例えば p-4 のようなクラスは要素の中に適用されません。Shadow DOM が、外側の CSS の規則を遮断するからです。ただし、CSS 変数は Shadow DOM の中にも継承されます。例えば、:root で定義した --brand-color は、要素の中でも var(--brand-color) で参照できます。
生成した CSS を要素の中に適用するには、要素のファイルでその CSS を with { type: "css" } を付けて import します。同じ CSS をレイアウトから普通の import で読み込むと、ページにも適用されます。
// vite.config.ts
import tailwindcss from "@tailwindcss/vite";
import { sobria } from "sobria/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [sobria(), tailwindcss()],
});/* src/styles/app.css */
@import "tailwindcss";// src/routes/layout.tsx
import type { LayoutProps } from "sobria/router";
import "../styles/app.css";
export default function Layout(props: LayoutProps) {
return <main class="p-4">{props.children}</main>;
}// src/elements/badge.tsx
import type { Ctx } from "sobria";
import tw from "../styles/app.css" with { type: "css" };
export default function Badge(props: { label: string }, { css }: Ctx) {
css(tw);
return <span class="rounded bg-blue-600 px-2 text-white">{props.label}</span>;
}多くの要素で同じ CSS を import するときは、モジュールで再エクスポートすると、import の記述を短縮できます。再エクスポートの文にも with { type: "css" } を記述し、再エクスポートしたモジュールを import する文には記述しません。
// src/styles/shared.ts
export { default } from "./app.css" with { type: "css" };// src/elements/badge.tsx
import tw from "../styles/shared.ts";@property を含む CSS
要素のシートに記述した @property の規則は、ブラウザに登録されません。@property は文書の全体に対して登録する規則だからです。Tailwind CSS は、影や変形などのユーティリティに @property を使用します。要素で Tailwind CSS を使用するときは、上の例のように、同じ CSS をレイアウトでも読み込みます。
ページとレイアウトの CSS
サーバーコンポーネントでは、Vite のアプリと同じく CSS を import します。
// src/routes/layout.tsx
import type { LayoutProps } from "sobria/router";
import "../styles/global.css";
export default function Layout(props: LayoutProps) {
return <main>{props.children}</main>;
}// src/routes/about/page.tsx
import "./about.css";
export default function Page() {
return <h1 class="title">sobria について</h1>;
}レイアウト、ページ、それらが import するモジュールが読み込んだ CSS を、sobria はページの <head> に <link rel="stylesheet"> として出力します。外側のレイアウトの CSS から順に出力し、ページの CSS を最後に出力します。同じ CSS は 1 回だけ出力します。
エラーのページの CSS
not-found.tsx と error.tsx のページを表示するときは、そのページと、そのページを囲むレイアウトの CSS を出力します。notFound() を投げたページや、エラーを投げたページ自身が import した CSS は出力しません。
// src/routes/not-found.tsx
import "./not-found.css";
export default function NotFound() {
return <h1 class="missing">ページが見つかりません</h1>;
}