Closed10

lit でも .css ファイルに CSS を書きたい

odanodan

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>`;
  }
}

https://lit.dev/docs/components/styles/

これは css-in-js の1つで、同様のアプローチとして Emotion がある

エディタの拡張機能を使えばテンプレートリテラルに対してもシンタックスハイライトが使えるので、そこそこ開発体験は良い

ただ個人的には .css ファイルにスタイルを書くほうがフルに言語のサポートが受けられたり、お手軽に CI でシンタックスチェックができたり[1]、エコシステムが整っているので好みだったりする
なので Lit でも .css ファイルにスタイルを書く方法について調べる

脚注
  1. Emotion スタイルだと tsserver でチェックしているため、tsc でチェックできない。https://github.com/styled-components/typescript-styled-plugin ↩︎

odanodan

こんな感じの ts ファイルを書いて

sample-lit-css.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 ファイルを書いて

sample.lit-css
:host {
  display: block;
}

h1 {
  color: hotpink;
}

Vite 向けのプラグインを用意すると動く

vite.config.ts
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()],
});
odanodan

他の方法について

静的な styles フィールドでスタイルの情報を公開する以外に <style> 要素を使う方法と、外部スタイルシートを読み込む方法がある

https://lit.dev/docs/components/styles/#styles-in-the-template

例えば前者だと .css ファイルを inline で読み込んで style 要素の中に埋め込むイメージ
こんな感じ

sample-style-tag.ts
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>`;
  }
}
odanodan

この方法だとカスタムエレメントのインスタンスごとにスタイルを用意できないという制約があるらしい

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.

odanodan

link タグを使って外部のスタイルシートを読み込むこともできる

https://lit.dev/docs/components/styles/#external-stylesheet

External styles can cause a flash-of-unstyled-content (FOUC) while they load.

って書いてるけどむしろ css-in-js だと js を評価しないとスタイルが適用されないし、そっちのほうが問題になりがちなイメージがある

odanodan

link タグを使う場合は、こんな感じのコードを書くと動く

sample-external-css.ts
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

sample-external-css.module.css
.root {
  display: block;
}

.title {
  color: hotpink;
}

この書き方は Vite の開発サーバでしか動かないので、build 時に href の値などを書き換えないといけない

odanodan

色々飛び道具なやり方を紹介してきたけど一番シンプルなのは、.css ファイルを import するたいていのバンドラはファイルの中身の文字列を返してくれるので、その文字列を unsafeCss にわたす方法だと思う

このスクラップは2024/01/05にクローズされました