- {(['light', 'dark'] as const).map((m) => (
+ {MODES.map(({ key, Icon, onPick }) => (
))}
-
+
);
diff --git a/frontend/src/app/components/theme/AccentColorPad.tsx b/frontend/src/app/components/theme/AccentColorPad.tsx
index 3a2e2919..afc76983 100644
--- a/frontend/src/app/components/theme/AccentColorPad.tsx
+++ b/frontend/src/app/components/theme/AccentColorPad.tsx
@@ -12,13 +12,21 @@ function stopToXY(hex: string): { x: number; y: number } | null {
return { x: hsl.h, y: Math.min(1, Math.max(0, (0.62 - hsl.l) / 0.34)) };
}
-// Arc's gradient engine, one control with two homes: 1-3 draggable stops on a hue/lightness field, + adds an analogous stop, - removes the newest. The first stop is the accent the tokens derive from; two or more stops become the canvas gradient wash. The pad reports stops and never knows who is listening.
+export interface WashControls {
+ opacity: number;
+ grain: number;
+ onOpacity: (v: number) => void;
+ onGrain: (v: number) => void;
+}
+
+// Arc/Zen gradient engine, one control with two homes: 1-3 draggable stops on a hue/lightness field, + adds a color-theory-harmonized stop (analogous, then triadic), - removes the newest. The first stop is the accent the tokens derive from; 2+ stops become the canvas gradient wash, whose intensity + grain the optional sliders tune. The pad reports stops and never knows who is listening.
const AccentColorPad: React.FC<{
c: ClaudeTokens;
stops: string[];
onChange: (stops: string[] | null) => void;
height?: number;
-}> = ({ c, stops, onChange, height = 240 }) => {
+ wash?: WashControls;
+}> = ({ c, stops, onChange, height = 240, wash }) => {
const padRef = useRef