外観

AIプロダクトの設定にある外観セクションです。テーマ、アクセント、背景の色合い、密度、チャットのフォント、文字サイズ、アクセシビリティのオプションを、保存するまで下書きとして保持します。

Appearanceは、アプリの見た目に関わるすべてを1つのセクションにまとめます。テーマカードには、選んだアクセントで描かれた各テーマの小さなウィンドウが表示されるので、保存する前に選択の違いがはっきりわかります。

Systemはデバイスの設定に従い、現在どちらになっているかを示します。High contrastはエッジとテキストを強調して背景をニュートラルに保つため、選択中は色合いのスライダーが無効になります。

アクセントの色相は実際の色のトラックで、その下に8つのクイック選択があり、読み出しには「Blue 250°」のように色の名前が表示されます。背景の色合いは、そのアクセントをサーフェスに混ぜます。密度とチャットフォントのカードには各オプションのサンプルが表示され、2つの文字サイズは上限と下限のあるステッパーです。

Reduce motionとReduce transparencyは、デバイスがすでにどちらかを求めている場合にそれを示します。

保存するまで、すべては下書きです。Settingsの保存バーは、何かが異なった時点で浮かび上がり、Discardで保存済みの外観に戻り、Resetはすべてのオプションをデフォルトに戻して、まだ破棄できる下書きにします。

  1. Proレジストリをcomponents.jsonに追加する

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  2. トークンを追加する

    アカウントページでトークンを作成し、.env.local に HEXTAUI_PRO_TOKEN として設定してください。

  3. ブロックを追加する

    pnpm dlx shadcn@latest add @hextaui-pro/appearance

APIに保存する

保存済みの値と、非同期のonSaveを渡します。バーでSaveが押されるまで、何も送信されません。エラーを投げると、そのメッセージがバーに表示され、選択内容は保持されます。

"use client"

import { IconPalette } from "@tabler/icons-react"

import { SettingsSection, SettingsShell } from "../settings/settings"
import { AppearanceSettings, type AppearanceValues } from "@/components/blocks/appearance/appearance"

export function AppearancePage({ appearance }: { appearance: AppearanceValues }) {
  return (
    <SettingsShell
      sections={[{ id: "appearance", label: "Appearance", icon: <IconPalette /> }]}
      className="h-svh"
    >
      <SettingsSection id="appearance">
        <AppearanceSettings
          values={appearance}
          onSave={async (values) => {
            const response = await fetch("/api/appearance", {
              method: "PUT",
              body: JSON.stringify(values),
            })
            if (!response.ok) throw new Error("Check your connection and try again.")
          }}
        />
      </SettingsSection>
    </SettingsShell>
  )
}

保存した外観を適用する

保存したら、アプリのテーマの仕組みに合わせて値を適用してください。normalizeAppearanceは古いバージョンが保存した内容を修復するため、不正な値でページが壊れることはありません。

"use client"

import * as React from "react"

import { AppearanceSettings, normalizeAppearance, type AppearanceValues } from "@/components/blocks/appearance/appearance"

function applyAppearance(values: AppearanceValues) {
  const root = document.documentElement
  const systemDark = window.matchMedia("(prefers-color-scheme: dark)").matches
  const dark = values.theme === "dark" || values.theme === "contrast" || (values.theme === "system" && systemDark)
  root.classList.toggle("dark", dark)
  root.dataset.contrast = values.theme === "contrast" ? "more" : "normal"
  root.dataset.density = values.density
  root.dataset.chatFont = values.chatFont
  root.dataset.motion = values.reduceMotion ? "reduce" : "full"
  root.dataset.transparency = values.reduceTransparency ? "reduce" : "full"
  root.style.setProperty("--accent-hue", String(values.hue))
  root.style.setProperty("--accent-tint", `${values.intensity}%`)
  root.style.setProperty("--text-interface", `${values.interfaceSize}px`)
  root.style.setProperty("--text-code", `${values.codeSize}px`)
}

export function AppearanceForm({ stored }: { stored: Partial<AppearanceValues> | null }) {
  const [saved, setSaved] = React.useState(() => normalizeAppearance(stored))

  React.useEffect(() => applyAppearance(saved), [saved])

  return (
    <AppearanceSettings
      values={saved}
      onSave={async (values) => {
        const response = await fetch("/api/appearance", {
          method: "PUT",
          body: JSON.stringify(values),
        })
        if (!response.ok) throw new Error("Check your connection and try again.")
        setSaved(values)
      }}
    />
  )
}

構造

外側から内側へ組み合わせるパーツ。

パーツ説明
AppearanceSettingsセクションの内容。シェルの保存バーにつながった、オプションのグループです。
normalizeAppearance保存された値や一部の値を、有効な値に変換します。サイズは範囲内に収め、色相は一周させ、未知の名前にはフォールバックします。
appearanceDefaults独自の値を渡さなかった場合に、Resetが戻す値。

AppearanceSettings

SettingsSectionの内側で使います。すべてのdivプロップも受け付けます。

プロパティ型デフォルト
values現在保存されている値: { theme, hue, intensity, density, chatFont, interfaceSize, codeSize, reduceMotion, reduceTransparency }。不正な値は修復され、その修復は未保存の変更として扱われません。
AppearanceValues–
onSave下書きを保存します。エラーを投げると、そのメッセージが保存バーに表示され、下書きは保持されます。
(values) => void | errors | Promise<void | errors>–
defaultsResetで戻される内容。
AppearanceValuesappearanceDefaults
プロパティ型デフォルト
themeSystemはprefers-color-schemeに従います。
"system" | "light" | "dark" | "contrast""system"
hueアクセントのOKLCH色相。0から359。
number250
intensityアクセントが背景をどれだけ染めるか。0から100。
number30
densityメッセージの間隔と内側の余白。
"compact" | "comfortable""comfortable"
chatFontメッセージのフォント。
"sans" | "serif" | "mono""sans"
interfaceSize文字サイズ(ピクセル)。12から18。
number14
codeSizeコードのサイズ(ピクセル)。11から20。
number13
reduceMotionスピナーとスライドするパネルを止めます。
booleanfalse
reduceTransparencyぼかしの代わりに、不透明な背景を使います。
booleanfalse
キーアクション
←→テーマ、密度、フォントのカードで、前または次のカードを選びます。色相トラックでは、1度動かします。
Shift→色相トラックで、15度動かします。Page UpとPage Downも同じ動作です。
HomeEnd色相トラックで、どちらかの端へ移動します。
↑↓文字サイズのフィールドで、サイズを1ピクセルずつ変更します。
⌘S未保存の変更があるときに保存します。WindowsとLinuxではCtrl+Sです。
  • 各カードグループは行の名前を持つradio groupで、各カードは自身のラベルで名前が付けられるため、スクリーンリーダーは「Dark, radio, 3 of 4」と読み上げます。
  • 色相トラックは「Blue, 250 degrees」のように色を名前で読み上げ、クイック選択は色の名前が付いたトグルボタンです。
  • Resetは実行後もフォーカスを保ち、デフォルトに戻ったことと保存が必要なことを読み上げます。

使用技術

Appearance を構成する無料のHextaUIコンポーネントです。それぞれ単独でインストールできます。

コード

2 個のファイルを components/blocks/appearance に追加しました。