Svelte でコンポーネントを作り React で糊付けする

<center>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187200/1cb4906b-6305-450b-98c6-b9ef8e896a9b.png" width="50%">
</center>

# React に不満はない

私は普段 React を存分に利用しています。特に不満はありません。
ただ、「皆さんに使ってもらえるような React 用コンポーネントでも作ろうかな」とか思うと、「なぜ React 専用にせにゃならんのだ」というキモチになることがあります。

# Svelte はコンポーネントづくりに向いている気がする

Svelte で作ったコンポーネントは、React や Vue、さらには Vanilla JS でも使えます（参照 [Svelteコンポーネント、なぜReact / Vue 上で動いてるの？](https://medium.com/eureka-engineering/svelte%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88-%E3%81%AA%E3%81%9Creact-vue-%E4%B8%8A%E3%81%A7%E5%8B%95%E3%81%84%E3%81%A6%E3%82%8B%E3%81%AE-9adab6246e77)）

厳密にいえば Svelte で作ったコンポーネントにも、Svelte のコアライブラリ的なものは必要なわけですから、React や Vue と比べてコアライブラリ的なものが軽量であるというだけなのかもしれませんが。

私が最近 React の中で利用した Svelte 製のコンポーネントは [josdejong/
svelte-jsoneditor](https://github.com/josdejong/svelte-jsoneditor) です。

私はこれを利用して感じたのは次のようなことです。
* React 禁止の案件でも使えるのでよいな。
* コンポーネントの初期化処理などがすっきりしていてよいな。
* Style がコンポーネント内に閉じ込められているので CSSを読み込んだりする手間がなくてよいな。

こんな感じで Svelte がコンポーネント作成に向いているのではないかと思った次第です。

# Style(CSS) に関して

Style(CSS) に関しては、私は React 上では Emotion を主として使っています。
全部 Emotion というわけではなく、適材適所でという感じで使っています。

Tailwind を使っていた時期もありましたが現在は使っていません。Tailwind は、タグ構造とスタイル指定を近づけることでソースファイルを見やすくすることがメリットだと思うのですが、Emotion で同じことが CSS の構文で実現できますので Emotion を使っています。
なお、私が React 内でどのように Emotion を利用しているかについては別の記事にしたいと思います。

さて、先ほど「Style がコンポーネント内に閉じ込められているので CSSを読み込んだりする手間がなくてよい」と書きましたが、逆にいうと Svelte 製のコンポーネントの CSS を弄りたくなったときは、古の CSS の適用順位、特に詳細度の知識が必要になります(色を変更する程度なら CSS Variables でも十分ですがそれは置いといて)。

例えば、`.wrapper .item { color: red; }` という CSS があった場合、`.my-item { color: blue; }` と書いても詳細度が低い（クラス指定の数が少ない）ため、青色にはならないわけです。`.wrapper .my-item { color: blue; }` とクラス指定を同じ数にすれば詳細度は同じになりますので青色になります。

まあ別に、常識的な知識の問題ですので、ふだん React + Emotion で楽をしている付けが回っただけといえそうですが、「Svelte でも Emotion 使えれば便利じゃないかな・・・」と思いました。

そこで試してみました。

# Svelte で Emotion を使う
百聞は一見に如かず、こちらの [SVELTE Play Ground](https://svelte.dev/playground/012c0077ef654a6e9f419a63fe16ef33?version=5.28.2) で、お試しいただけます。

ふだん React などを利用している方なら、上記 Play Ground だけで十分であると思いますが、一応簡単に解説します。

``` App.svelte
<script>
	import Styled from './Styled.svelte';
	import Flex from './Flex.svelte';
	import FlexCol from './FlexCol.svelte';
</script>

<Flex sx={{gap: '1rem'}}>
	<Styled as="div" class="test" sx={`color:red`}>
		Hello
	</Styled>
	<Styled as="div" sx={`color:green`}>
		World!
	</Styled>
</Flex>

<FlexCol sx={{marginTop: '1rem'}}>
	<Styled as="div" sx={`color:red`}>
		Hello
	</Styled>
	<Styled as="div" sx={`color:green`}>
		World!
	</Styled>
</FlexCol>
```

`App.svelte` がアプリのメインです。`as` や `sx` prop は、MUI というフレームワークの命名を参考に命名しました。
`as` でタグ名を、`sx` で Emotion に渡すスタイルを指定します。

``` Styled.svelte
<script>
	import {css} from '@emotion/css';

	const {sx = {}, as, children, class: className} = $props();
	const _sx = css(sx);
</script>

<svelte:element this={as || 'div'} class={[className, _sx]}>
	{@render children?.()}
</svelte:element>
```

Emotion を汎用的に使えるようにした `Styled.svelte` のソースはこれだけです。自分でいうのもなんですが素敵です。
`as` や `sx` を変更しての再レンダリングは今のところ想定しておりません。何分 Svelte はほぼ初心者ですのでアドバイスお待ちしております。

`Flex.svelte` 、`FlexCol.svelte` はおまけです。`Style.svelte` を拡張して使うだけのものなのでなくても構いません。

# 最後に

Let’s have fun with React and Svelte!