lit でも .css ファイルに CSS を書きたい
Lit でスタイルを実装する場合、タグ付きテンプレートを提供する関数の css
を使用してスタイルを定義する
import {LitElement, html, css} from 'lit';
import {customElement} from 'lit/decorators.js';
@customElement('my-element')
export class MyElement extends LitElement {
static styles = css`
p {
color: green;
}
`;
protected render() {
return html`<p>I am green!</p>`;
}
}
これは css-in-js の1つで、同様のアプローチとして Emotion がある
エディタの拡張機能を使えばテンプレートリテラルに対してもシンタックスハイライトが使えるので、そこそこ開発体験は良い
ただ個人的には .css ファイルにスタイルを書くほうがフルに言語のサポートが受けられたり、お手軽に CI でシンタックスチェックができたり[1]、エコシステムが整っているので好みだったりする
なので Lit でも .css ファイルにスタイルを書く方法について調べる
-
Emotion スタイルだと tsserver でチェックしているため、tsc でチェックできない。https://github.com/styled-components/typescript-styled-plugin ↩︎
今回の目的を達成するパッケージに lit-css がある
rollup や esbuild 向けのプラグインが提供されている
こんな感じの ts ファイルを書いて
import { LitElement, html } from "lit";
import { customElement } from "lit/decorators.js";
import style from "./sample.lit-css";
@customElement("sample-lit-css")
export class SampleLitCss extends LitElement {
static readonly styles = [style];
render() {
return html`<h1>It's Lit!</h1>`;
}
}
こんな感じの .lit-css ファイルを書いて
:host {
display: block;
}
h1 {
color: hotpink;
}
Vite 向けのプラグインを用意すると動く
import { defineConfig, Plugin } from "vite";
import { transform } from "@babel/core";
import { transform as litCssTransform } from "@pwrs/lit-css";
function babelPlugin(): Plugin {
return {
name: "babel-plugin",
transform(code, id) {
if (id.endsWith(".ts")) {
const result = transform(code);
return result?.code;
}
return code;
},
};
}
function litCssPlugin(): Plugin {
return {
name: "lit-css-plugin",
async transform(code, id) {
if (id.endsWith(".lit-css")) {
const result = await litCssTransform({ css: code });
return result;
}
},
};
}
export default defineConfig({
plugins: [babelPlugin(), litCssPlugin()],
});
lit-css の仕組みはそんなに複雑ではない
対象の CSS ファイルの中身を lit の css 関数に渡すだけ
似たようなアプローチは material-web でも取られている
他の方法について
静的な styles フィールドでスタイルの情報を公開する以外に <style>
要素を使う方法と、外部スタイルシートを読み込む方法がある
例えば前者だと .css ファイルを inline で読み込んで style 要素の中に埋め込むイメージ
こんな感じ
import { LitElement, html } from "lit";
import { customElement } from "lit/decorators.js";
import style from "./sample-style-tag.css?inline";
@customElement("sample-style-tag")
export class SampleLitCss extends LitElement {
render() {
return html`<div>
<style>
${style}
</style>
<h1>sample-style-tag</h1>
</div>`;
}
}
この方法だとカスタムエレメントのインスタンスごとにスタイルを用意できないという制約があるらしい
Limitations in the ShadyCSS polyfill around per instance styling. Per instance styling is not supported using the ShadyCSS polyfill. See the ShadyCSS limitations for details.
link タグを使って外部のスタイルシートを読み込むこともできる
External styles can cause a flash-of-unstyled-content (FOUC) while they load.
って書いてるけどむしろ css-in-js だと js を評価しないとスタイルが適用されないし、そっちのほうが問題になりがちなイメージがある
link タグを使う場合は、こんな感じのコードを書くと動く
import { LitElement, html } from "lit";
import { customElement } from "lit/decorators.js";
import classes from "./sample-external-css.module.css";
@customElement("sample-external-css")
export class SampleExternalCss extends LitElement {
render() {
return html`
<div class=${classes.root}>
<link
rel="stylesheet"
crossorigin
href="/src/components/sample-external-css/sample-external-css.module.css"
/>
<h1 class=${classes.title}>sample-external-css</h1>
</div>
`;
}
}
css
.root {
display: block;
}
.title {
color: hotpink;
}
この書き方は Vite の開発サーバでしか動かないので、build 時に href の値などを書き換えないといけない
色々飛び道具なやり方を紹介してきたけど一番シンプルなのは、.css ファイルを import するたいていのバンドラはファイルの中身の文字列を返してくれるので、その文字列を unsafeCss
にわたす方法だと思う