94 lines
3.2 KiB
TypeScript
94 lines
3.2 KiB
TypeScript
"use client";
|
|
import { RotateCcw } from "lucide-react";
|
|
import { useSettings } from "./SettingsProvider";
|
|
|
|
const DEFAULT_PRIMARY_HEX = "#4dc4d4";
|
|
const DEFAULT_SECONDARY_HEX = "#b772f0";
|
|
|
|
export function AccentColorPickers() {
|
|
const { settings, set } = useSettings();
|
|
|
|
return (
|
|
<div className="space-y-3">
|
|
<ColorRow
|
|
label="Primary Accent"
|
|
description="Used for buttons, toggles, focus rings, and the cyan side of accent gradients."
|
|
value={settings.accentPrimary}
|
|
fallback={DEFAULT_PRIMARY_HEX}
|
|
onChange={(v) => set("accentPrimary", v)}
|
|
onReset={() => set("accentPrimary", "")}
|
|
/>
|
|
<ColorRow
|
|
label="Secondary Accent"
|
|
description="Used for the violet side of accent gradients, glows, and ambient page lighting."
|
|
value={settings.accentSecondary}
|
|
fallback={DEFAULT_SECONDARY_HEX}
|
|
onChange={(v) => set("accentSecondary", v)}
|
|
onReset={() => set("accentSecondary", "")}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ColorRow({
|
|
label,
|
|
description,
|
|
value,
|
|
fallback,
|
|
onChange,
|
|
onReset,
|
|
}: {
|
|
label: string;
|
|
description?: string;
|
|
value: string;
|
|
fallback: string;
|
|
onChange: (v: string) => void;
|
|
onReset: () => void;
|
|
}) {
|
|
const isDefault = value === "";
|
|
const display = value || fallback;
|
|
|
|
return (
|
|
<div className="flex items-start justify-between gap-4 py-2">
|
|
<div className="min-w-0">
|
|
<div className="text-sm font-medium">{label}</div>
|
|
{description && (
|
|
<div className="text-xs text-[var(--color-fg-muted)] mt-0.5">{description}</div>
|
|
)}
|
|
<div className="text-[10px] font-mono uppercase tracking-wider text-[var(--color-fg-muted)] mt-1">
|
|
{isDefault ? "default" : display}
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-2 flex-shrink-0">
|
|
<label
|
|
className="relative w-9 h-9 rounded-lg border border-[var(--color-glass-border-strong)] bg-[var(--color-glass)] cursor-pointer overflow-hidden grid place-items-center hover:border-[color-mix(in_oklch,var(--color-cyan)_50%,var(--color-glass-border))] transition-colors"
|
|
title={`Change ${label.toLowerCase()}`}
|
|
>
|
|
<span
|
|
className="absolute inset-1 rounded-md"
|
|
style={{ background: display }}
|
|
aria-hidden
|
|
/>
|
|
<input
|
|
type="color"
|
|
value={display}
|
|
onChange={(e) => onChange(e.target.value.toLowerCase())}
|
|
className="absolute inset-0 opacity-0 cursor-pointer"
|
|
aria-label={label}
|
|
/>
|
|
</label>
|
|
<button
|
|
type="button"
|
|
onClick={onReset}
|
|
disabled={isDefault}
|
|
aria-label={`Reset ${label.toLowerCase()}`}
|
|
title="Reset to default"
|
|
className="w-9 h-9 grid place-items-center rounded-lg border border-[var(--color-glass-border-strong)] bg-[var(--color-glass)] text-[var(--color-fg-dim)] hover:text-[var(--color-fg)] hover:border-[color-mix(in_oklch,var(--color-cyan)_50%,var(--color-glass-border))] transition-colors disabled:opacity-30 disabled:cursor-not-allowed disabled:hover:border-[var(--color-glass-border-strong)]"
|
|
>
|
|
<RotateCcw className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|