複数テーマの並置
現状の使い方:ThemeProvider のネスト
Section titled “現状の使い方:ThemeProvider のネスト”import { ThemeProvider, createTheme, defaultTheme } from "@hirasaki1985/web-ui";
const partnerTheme = createTheme({ colors: { primary: { /* パートナーB社の10段 */ } } });
const Page = () => ( <ThemeProvider theme={defaultTheme}> <MainContent /> {/* 自社ブランド */}
<ThemeProvider theme={partnerTheme}> <PartnerWidget /> {/* パートナーB社ブランドだけここで上書き */} </ThemeProvider> </ThemeProvider>);ThemeProvider はReactのContextでテーマを配信しているため、ネストすると内側の値が優先されます(Reactの標準的なContextの挙動です)。useTheme() はコンポーネントから見て最も近い祖先の ThemeProvider の値を返すため、PartnerWidget 配下だけがパートナーブランドで描画され、それより外側は自社ブランドのままです。
styled-componentsの ThemeProvider も同時に配信しているため(src/theme/ThemeProvider.tsx)、useTheme() を使わずstyled-componentsの props.theme だけを参照しているコンポーネントも同様にスコープが効きます。
計画中:CSS変数のスコープ切り
Section titled “計画中:CSS変数のスコープ切り”CSS配布が実装されると、任意のDOM要素にインラインスタイルとしてCSS変数をスプレッドすることでもスコープを切れるようになる計画です。
// 計画中のイメージ(未実装)<div style={themeToCssVars(partnerTheme)}> <PartnerWidget /></div>CSSカスタムプロパティはDOM上で子孫要素へ継承されるため、style でCSS変数を上書きした要素の内側だけ、そのCSS変数を参照しているスタイル(var(--wui-bg-brand) 等)が新しい値に切り替わります。外側や兄弟要素には影響しません。React Contextによるスコープとほぼ同じ考え方ですが、CSSだけで完結する分、styled-components を経由しないプレーンなCSS/Tailwindクラスにも同じ切り替えが効くようになる点が異なります。
Portalを使う部品への注意
Section titled “Portalを使う部品への注意”Dialog / PopupMenu / Dropdown などはPortal(DOMツリー上の別の場所への描画)を使っていません。素の position: fixed で自身の位置に描画されます。そのため、現状の ThemeProvider ネストによるスコープ切りは、これらの部品にもそのまま効きます(React Contextのスコープは実際のDOM構造ではなく、コンポーネントツリー上の位置で決まるため)。
将来これらの部品がPortal(document.body 直下への描画等)を採用する場合は注意が必要です。Portal先のDOMはReactツリー上の親を維持するため React Contextは引き続き正しく効きますが、CSS変数のスコープ切り(計画中の方式)はDOMツリー上の親子関係で決まるため、Portal先が themeToCssVars() を当てた要素の外側に出ると、スコープが外れてしまいます。Portalを採用する際は、Portal先のルート要素にも同じCSS変数を当てる実装が必要になります。