nyuchimzizi
Mzizi — an open-architecture project of the Bundu Foundation, operated and developed by Nyuchi. Built on the Five African Minerals palette.
Built by Nyuchi Africav4.0.39
Reader customization panel for font size, line height, theme (light/dark/sepia), font family (Noto Sans/Serif/system), and text alignment. Persisted per-user in the Digital Twin preferences. Used in Novels reader and News article reader mode.
View the full component source code below.
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
interface FontSettings {
fontSize: number
lineHeight: number
fontFamily: "sans" | "serif" | "system"
theme: "light" | "dark" | "sepia"
}
interface FontSettingsPanelProps extends React.ComponentProps<"div"> {
settings: FontSettings
onChange: (settings: FontSettings) => void
}
const themes = [
{ key: "light" as const, label: "Light", bg: "bg-white", text: "text-black" },
{ key: "dark" as const, label: "Dark", bg: "bg-[#1A1A1A]", text: "text-white" },
{ key: "sepia" as const, label: "Sepia", bg: "bg-[#F4ECD8]", text: "text-[#5B4636]" },
]
function FontSettingsPanel({ settings, onChange, loading = false, className, ...props }: FontSettingsPanelProps) {
const update = (partial: Partial<FontSettings>) => onChange({ ...settings, ...partial })
return (
<div data-slot="font-settings-panel" data-portal="https://design.nyuchi.com/components/font-settings-panel" className={cn("space-y-4 rounded-[var(--radius-xl,17px)] border border-border bg-card p-5", className)} {...props}>
{/* Font size */}
<div>
<label className="text-xs font-medium text-muted-foreground">Font Size</label>
<div className="mt-1.5 flex items-center gap-3">
<span className="text-xs text-muted-foreground">A</span>
<input type="range" min={12} max={28} value={settings.fontSize} onChange={e => update({ fontSize: parseInt(e.target.value) })}
className="h-1 flex-1 accent-primary" aria-label="Font size" />
<span className="text-lg text-muted-foreground">A</span>
<span className="w-8 text-right text-xs tabular-nums">{settings.fontSize}px</span>
</div>
</div>
{/* Line height */}
<div>
<label className="text-xs font-medium text-muted-foreground">Line Spacing</label>
<div className="mt-1.5 flex items-center gap-3">
<input type="range" min={1.2} max={2.4} step={0.1} value={settings.lineHeight}
onChange={e => update({ lineHeight: parseFloat(e.target.value) })}
className="h-1 flex-1 accent-primary" aria-label="Line height" />
<span className="w-8 text-right text-xs tabular-nums">{settings.lineHeight.toFixed(1)}</span>
</div>
</div>
{/* Font family */}
<div>
<label className="text-xs font-medium text-muted-foreground">Font</label>
<div className="mt-1.5 flex gap-1.5">
{([
{ key: "serif" as const, label: "Serif", family: "var(--font-serif, serif)" },
{ key: "sans" as const, label: "Sans", family: "var(--font-sans, sans-serif)" },
{ key: "system" as const, label: "System", family: "system-ui" },
]).map(f => (
<button key={f.key} onClick={() => update({ fontFamily: f.key })} aria-pressed={settings.fontFamily === f.key}
className={cn("h-10 flex-1 rounded-full text-sm font-medium transition-colors",
settings.fontFamily === f.key ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"
)} style={{ fontFamily: f.family }}>
{f.label}
</button>
))}
</div>
</div>
{/* Reader theme */}
<div>
<label className="text-xs font-medium text-muted-foreground">Theme</label>
<div className="mt-1.5 flex gap-1.5">
{themes.map(t => (
<button key={t.key} onClick={() => update({ theme: t.key })} aria-pressed={settings.theme === t.key}
className={cn("flex h-10 flex-1 items-center justify-center rounded-full text-xs font-medium transition-all",
t.bg, t.text, settings.theme === t.key && "ring-2 ring-primary"
)}>
{t.label}
</button>
))}
</div>
</div>
</div>
)
}
export { FontSettingsPanel }
export type { FontSettingsPanelProps, FontSettings }
npx shadcn@latest add https://mzizi.dev/api/v1/ui/font-settings-panelFetch this component's metadata and source code from the registry API.
/api/v1/ui/font-settings-panel