Aparência

A seção Aparência das configurações de um produto de IA: tema, cor de destaque, tom do plano de fundo, densidade, fonte do chat, tamanhos de texto e opções de acessibilidade, tudo mantido como rascunho até que as pessoas salvem.

O Appearance reúne tudo sobre a aparência do app em uma única seção. Os cards de tema mostram uma pequena janela em cada tema, desenhada na cor de destaque que você escolheu, para que a escolha fique clara antes de salvar.

System segue o dispositivo e informa qual é no momento. High contrast reforça bordas e texto e mantém os fundos neutros, então o slider de tom é desativado enquanto ele estiver selecionado.

O matiz do destaque é uma trilha de cor de verdade com oito escolhas rápidas abaixo, e o indicador nomeia a cor, como Blue 250°. O tom do plano de fundo mistura esse destaque às superfícies. Os cards de densidade e de fonte do chat mostram um exemplo de cada opção, e os dois tamanhos de texto são steppers com limites.

Reduce motion e Reduce transparency informam quando o dispositivo já pede uma das duas.

Tudo é um rascunho até ser salvo. A barra de salvar do Settings sobe assim que algo difere, Discard traz de volta a aparência salva e Reset devolve todas as opções ao padrão como um rascunho que você ainda pode descartar.

  1. Adicione o registro Pro ao components.json

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  2. Adicione seu token

    Crie um token na sua página de conta e coloque-o em .env.local como HEXTAUI_PRO_TOKEN.

  3. Adicione o bloco

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

Salve na sua API

Passe os valores salvos e um onSave assíncrono. Nada é enviado até alguém pressionar Save na barra. Lance um erro para mostrar sua mensagem na barra e manter as escolhas da pessoa.

"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>
  )
}

Aplique a aparência salva

Depois de salvo, aplique os valores ao seu app da forma como ele aplica temas. normalizeAppearance corrige qualquer coisa armazenada por uma versão mais antiga, para que um valor inválido nunca quebre a página.

"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)
      }}
    />
  )
}

Anatomia

As partes que você compõe, de fora para dentro.

ParteDescrição
AppearanceSettingsO conteúdo da seção: os grupos de opções, ligados à barra de salvar do shell.
normalizeAppearanceTransforma valores armazenados ou parciais em valores válidos: limita os tamanhos, dá a volta no matiz e usa um fallback para nomes desconhecidos.
appearanceDefaultsOs valores para os quais o Reset volta quando você não passa os seus.

AppearanceSettings

Use dentro de um SettingsSection. Também aceita todas as props de div.

PropTipoPadrão
valuesO que está salvo agora: { theme, hue, intensity, density, chatFont, interfaceSize, codeSize, reduceMotion, reduceTransparency }. Valores inválidos são corrigidos, e a correção não conta como uma alteração não salva.
AppearanceValues–
onSaveSalva o rascunho. Lance um erro para mostrar a mensagem na barra de salvar e manter o rascunho.
(values) => void | errors | Promise<void | errors>–
defaultsO que o Reset restaura.
AppearanceValuesappearanceDefaults
PropTipoPadrão
themeSystem segue o prefers-color-scheme.
"system" | "light" | "dark" | "contrast""system"
hueO matiz OKLCH do destaque, de 0 a 359.
number250
intensityO quanto do destaque tinge os fundos, de 0 a 100.
number30
densityEspaço entre as mensagens e dentro delas.
"compact" | "comfortable""comfortable"
chatFontA fonte das mensagens.
"sans" | "serif" | "mono""sans"
interfaceSizeTamanho do texto em pixels, de 12 a 18.
number14
codeSizeTamanho do código em pixels, de 11 a 20.
number13
reduceMotionPara os spinners e os painéis deslizantes.
booleanfalse
reduceTransparencyFundos sólidos em vez de desfocados.
booleanfalse
TeclaAção
←→Nos cards de tema, densidade ou fonte, escolhe o card anterior ou o próximo. Na trilha de matiz, move um grau.
Shift→Na trilha de matiz, move 15 graus. Page Up e Page Down fazem o mesmo.
HomeEndNa trilha de matiz, salta para qualquer uma das extremidades.
↑↓Em um campo de tamanho de texto, altera o tamanho em um pixel.
⌘SSalva enquanto algo não está salvo. Ctrl+S no Windows e no Linux.
  • Cada grupo de cards é um radio group nomeado por sua linha, e cada card é nomeado pelo próprio rótulo, então os leitores de tela anunciam "Escuro, botão de opção, 3 de 4".
  • A trilha de matiz lê sua cor pelo nome, como "Azul, 250 graus", e as escolhas rápidas são botões de alternância nomeados pela cor.
  • Reset mantém o foco depois de executar e anuncia que os padrões voltaram e precisam ser salvos.

Construído com

Os componentes gratuitos do HextaUI de que Appearance é feito. Cada um é instalado separadamente.

Código

2 arquivos, adicionados a components/blocks/appearance.