Backstage の配色を変更する

# はじめに

Backstage はデフォルトで紫基調ですが、**`custom-theme.css` を `App.tsx` と同じ階層に置くだけ** でヘッダー・サイドバー・ボタンなど主要 UI の色を簡単に変えられます。  


## 1. `custom-theme.css` の作成

### 1.1 `custom-theme.css`ファイルの作成
以下のディレクトリにカスタム用のCSSファイルを作成します。
```text
packages/app/src/
├─ App.tsx
└─ custom-theme.css   ← この記事で示す CSS を追加
````

次に`App.tsx` に 1 行追記します。

```tsx
import './custom-theme.css';
```

### 1.2 `custom-theme.css`ファイルの編集
長いですが、以下のようにCSSを設定することで **ヘッダー・サイドバー・ボタン**などが一括でブランドカラーになります。
初期設定では全て白色にしています。自由に色をいじってみてください。

```css
/* ヘッダーの背景色の設定 */
header {
  background: white !important; /* Orange */
}
 
/* サイドバーの背景色の設定 */
.BackstageSidebar-drawer,
[class*="BackstageSidebar-drawer"],
.BackstageSidebar-drawer:hover,
[class*="BackstageSidebar-drawer"]:hover,
.MuiBox-root[class*="BackstageSidebar-drawer"] {
  background-color: white !important;
}
 
/* サイドバーのアイテムのテキストの色の設定 */
[class*="BackstageSidebarItem-root"],
.BackstageSidebarItem-root,
[class*="BackstageSidebarItem-root"] span,
.BackstageSidebarItem-root span,
[class*="BackstageSidebarItem-root"] div,
.BackstageSidebarItem-root div,
[class*="BackstageSidebarItem-root"] a,
.BackstageSidebarItem-root a {
  color: white !important;
}
 
/* サイドバーのアイテムのホバー時の背景色を設定 */
[class*="BackstageSidebarItem-root"]:hover,
.BackstageSidebarItem-root:hover,
[class*="BackstageSidebarItem-selected"]:hover,
.BackstageSidebarItem-selected:hover {
  background-color: white !important;
}
 
/* サイドバーのアイテムの選択時の背景色を設定 */
[class*="BackstageSidebarItem-selected"],
.BackstageSidebarItem-selected {
  background-color: white !important;
}
 
/* 選択されたサイドバーアイテムの左側の縦線の色を設定 */
[class*="BackstageSidebarItem-selected"]::before,
.BackstageSidebarItem-selected::before,
[class*="BackstageSidebarItem-selected"]::after,
.BackstageSidebarItem-selected::after,
[class*="BackstageSidebarItem-selected"],
.BackstageSidebarItem-selected {
  border-left-color: white !important;
}
 
/* サイドバーのディバイダーの色を設定 */
[class*="BackstageSidebarDivider-root"],
.BackstageSidebarDivider-root {
  background-color: white !important;
}
 
/* カードヘッダー内のテキストの色の設定 */
.BackstageItemCardHeader-root h3,
.BackstageItemCardHeader-root h4,
.BackstageItemCardHeader-root div,
[class*="BackstageItemCardHeader-root"] h3,
[class*="BackstageItemCardHeader-root"] h4,
[class*="BackstageItemCardHeader-root"] div,
.makeStyles-header h3,
.makeStyles-header h4,
.makeStyles-header div,
[class*="makeStyles-header"] h3,
[class*="makeStyles-header"] h4,
[class*="makeStyles-header"] div {
  color: white !important;
  text-shadow: none !important;
}
 
/* Home画面のCreateボタンの色の設定 */
a[href="/create"][class*="MuiButtonBase-root"][class*="MuiButton-contained"][class*="MuiButton-containedPrimary"] {
  background-color: white !important;
  color: white !important;
}
 
/* Register Existing Component ボタンの色の設定 */
a[href="/catalog-import"].MuiButtonBase-root.MuiButton-root.MuiButton-outlined.MuiButton-outlinedPrimary,
a[href="/catalog-import"],
button[data-testid="template-card-actions--create"].MuiButtonBase-root.MuiButton-root.MuiButton-outlined.MuiButton-outlinedPrimary,
button[data-testid="template-card-actions--create"] {
  background-color: white !important; /* DarkOrange */
  color: white !important;
  border-color: white !important;
}
 
/* Register Existing Component ボタンのホバー時の色も設定 */
a[href="/create"][class*="MuiButtonBase-root"][class*="MuiButton-contained"][class*="MuiButton-containedPrimary"]:hover {
  opacity: 0.9 !important;
}
a[href="/catalog-import"].MuiButtonBase-root.MuiButton-root.MuiButton-outlined.MuiButton-outlinedPrimary:hover,
a[href="/catalog-import"]:hover,
button[data-testid="template-card-actions--create"].MuiButtonBase-root.MuiButton-root.MuiButton-outlined.MuiButton-outlinedPrimary:hover,
button[data-testid="template-card-actions--create"]:hover {
  background-color: white !important; /* Yellow */
  opacity: 0.9 !important;
}


/* テンプレート内の色の設定 */
/* テンプレート内のNextボタン要素を黄色に設定 */
button[class*="MuiButtonBase-root"][class*="MuiButton-root"][class*="MuiButton-contained"][class*="MuiButton-containedPrimary"] {
  background-color: white !important;
  color: white !important;
}
 
/* アクティブなステップの色の設定 */
svg[class*="MuiStepIcon-active"] circle {
  fill: white !important;
}

/* 完了したステップがある場合も#ffb600に設定 */
svg[class*="MuiStepIcon-completed"] path {
  fill: white !important;
}
```

## 2. テンプレートカードを“カテゴリ別”に色分けしたい場合

下図のようなBackstage のテンプレートカード（Service / Website / Library など）を
**カテゴリごとに背景色を変えたい**ときは、CSS だけではタグ名に応じた class が付いていないため判別できません。

そこで Javascriptファイル追加し、カードごとにカスタム class を付与してから CSS で色を当てます。

![image (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4096150/8f80374d-d0cd-4473-97ff-c9cda3ea6590.png)



### 2.1 `card-colorizer.js`ファイルの作成

`App.tsx`と同じディレクトリにJSファイルを新規作成します。
```
packages/app/src/
├─ App.tsx
├─ custom-theme.css
└─ card-colorizer.js   ← カードに class を付与
```

次に、`App.tsx` に追加インポートします。

```tsx
import './card-colorizer.js';
```


### 2.2 クラス名の動的付与
JSファイル内を以下のように編集します。

```js
// タグごとにカードの色を変更するスクリプト
document.addEventListener('DOMContentLoaded', function() {
  // カードの色分け処理を実行する関数
  const applyCardColors = () => {
    // カードを全て取得（クラス名の一部一致で検索）
    const cards = document.querySelectorAll('[class*="MuiPaper-root"][class*="MuiCard-root"]');
    
    // 各カードに対して処理
    cards.forEach(card => {
      // タグのテキストを取得（makeStyles-subtitleWrapperクラスを含む要素の最初のdiv）
      const subtitleWrapper = card.querySelector('[class*="makeStyles-subtitleWrapper"]');
      
      if (subtitleWrapper) {
        const tagElement = subtitleWrapper.querySelector('div:first-child');
        
        if (tagElement) {
          const tagText = tagElement.textContent.trim().toLowerCase();
          
          // タグに基づいてカードにカスタムクラスを追加
          card.classList.add(`backstage-card-${tagText}`);
          
          // ヘッダー部分にもクラスを追加（より細かいスタイリングのため）
          const header = card.querySelector('[class*="BackstageItemCardHeader-root"]');
          if (header) {
            header.classList.add(`backstage-card-header-${tagText}`);
          }
        }
      }
    });
  };

  // 初期実行
  setTimeout(applyCardColors, 500);

  // DOMの変更を監視して新しいカードが追加されたときに色を適用
  const observer = new MutationObserver((mutations) => {
    let shouldApply = false;
    
    mutations.forEach((mutation) => {
      if (mutation.addedNodes.length > 0) {
        shouldApply = true;
      }
    });
    
    if (shouldApply) {
      // 少し遅延させて実行（DOMが完全に更新された後に実行するため）
      setTimeout(applyCardColors, 100);
    }
  });

  // DOMの変更を監視開始
  observer.observe(document.body, { childList: true, subtree: true });
  
  // ページ遷移やタブ切り替え時にも実行するため、イベントリスナーを追加
  window.addEventListener('popstate', () => setTimeout(applyCardColors, 500));
  window.addEventListener('hashchange', () => setTimeout(applyCardColors, 500));
});
```

### 2.3 カテゴリ別の色を CSS で定義
カスタムCSSファイルにて、動的付与されたクラス名に応じたカードヘッダーの色を設定します。

```css
/* custom-theme.css の末尾に追記 */

/* テンプレートのヘッダーの色の設定 */
/* Library タグのカード */
.backstage-card-header-library.BackstageItemCardHeader-root,
.backstage-card-header-library[class*="BackstageItemCardHeader-root"],
.backstage-card-header-library.makeStyles-header,
.backstage-card-header-library[class*="makeStyles-header"],
div[class*="MuiBox-root"][class*="BackstageItemCardHeader-root"][class*="makeStyles-header"].backstage-card-header-library {
  background: white !important; 
  color: white !important;
}
 
/* Website タグのカード */
.backstage-card-header-website.BackstageItemCardHeader-root,
.backstage-card-header-website[class*="BackstageItemCardHeader-root"],
.backstage-card-header-website.makeStyles-header,
.backstage-card-header-website[class*="makeStyles-header"],
div[class*="MuiBox-root"][class*="BackstageItemCardHeader-root"][class*="makeStyles-header"].backstage-card-header-website {
  background: white !important; 
  color: white !important;
}
 
/* Service タグのカード */
.backstage-card-header-service.BackstageItemCardHeader-root,
.backstage-card-header-service[class*="BackstageItemCardHeader-root"],
.backstage-card-header-service.makeStyles-header,
.backstage-card-header-service[class*="makeStyles-header"],
div[class*="MuiBox-root"][class*="BackstageItemCardHeader-root"][class*="makeStyles-header"].backstage-card-header-service {
  background: white !important;
  color: white !important;
}
```

> **注意**
> カスタム class は描画後に JS が付与するため、
> ページ遷移直後に一瞬デフォルト色が見える “フラッシュ” が発生します

## 3. まとめ

今回は以下のようにBackstageの配色を変更しました。
他に配色変更の良い方法があれば教えていただきたいです。
1. **基本の配色変更** ― `custom-theme.css` を置いて `import` するだけ
2. **カテゴリ別カード色** ― `card-colorizer.js` で class 付与 → CSS で着色