KODKAFA Update: From UX Shift to Terminol and a Smarter Theme System
4 min read
The homepage of KODKAFA used to look and feel a lot like a polished corporate SaaS site. It was clean, structured and safe—but it didn’t really represent what KODKAFA is: a personal engineering sandbox.
Worse, that kind of UI throws everything at the user upfront: navigation, menus, sections, CTAs. It’s easy for people to lose focus, and just as easy for us to quietly steer their attention toward our own goals instead of theirs.
After the AI era, people became much more comfortable asking questions and explicitly explaining their intent. They describe what they want to do and expect the system to respond. So the obvious question is:
If users are already used to expressing intent,
why are we still interacting with them like a simple static website?
That’s the idea behind the UX change for KODKAFA: instead of dumping all options on the screen and hoping for the right click, the interface waits for intent and reacts to it—closer to a prompt that can evolve into a smarter, more adaptive interaction model over time.
Enter Terminol: Intent-First, Terminal-Like UX
To support this shift, KODKAFA moved to a terminal-like, intent-first UX powered by Terminol.
Instead of presenting a long “catalog” of UI and options, the interface now:
waits for the user’s intent,
responds to commands,
and behaves more like an interactive prompt than a static dashboard.
That means:
we don’t show everything up front,
we react to what the user asks for,
and the surface can become smarter over time, evolving towards a more “prompt-like” experience.
You can read the full story behind Terminol here:
Story & background: https://goker.me/terminol
Live UX: https://kodkafa.com
Code: https://github.com/kodkafa/terminol
Once Terminol became the primary surface for KODKAFA, the existing visual system had to catch up.
The Balanced Theme System (and Its Limits)
A little over 200 days ago I shipped the first version of the Balanced Theme System – a slider-based theming layer that let users nudge hue, chroma, contrast and radius without touching a color picker, while staying inside WCAG contrast bounds. It was built on top of Tailwind v4, oklch() and shadcn/ui’s theme tokens.
For a classic “page + components” layout, that was enough.
But with an intent-first terminal surface front and center, new requirements appeared:
clearer semantic colors (prompt, success, warning, error, info),
consistent behavior across the entire palette when you shift hue or chroma,
and more “breathing room” in a UI that is mostly text.
The original system lived primarily in the shadcn token set. The sliders could move --primary, --background, etc., but they didn’t really repaint the entire palette the way an intent-first terminal needed.
In other words: I needed the sliders to reach more than just --primary and --background.
Spreading Color Across the Palette
The next step was to teach the theme system how to repaint the palette itself, not just a couple of core tokens.
The getThemeColorValue helper is the small piece that made that possible:
export function getThemeColorValue(color: TailwindColor,shade = 500,): string {const base = COLOR_MAP[color] || COLOR_MAP.indigo;// Logic:// L: Base Lightness// C: Base Chroma * var(--theme-chroma-factor, 1)// H: Base Hue + var(--theme-hue-shift, 0)// Note: We use CSS calc for runtime dynamismreturn `oklch(${base.l.toFixed(3)} calc(${base.c.toFixed(3,)} * var(--theme-chroma-factor, 1)) calc(${base.h.toFixed(1,)} + var(--theme-hue-shift, 0)))`;}
Instead of hard-coding color values, each palette color is now generated in OKLCH with two global levers:
--theme-chroma-factor– how intense/saturated the palette should feel--theme-hue-shift– where the entire palette sits on the hue wheel
Both are controlled by the same theme system you’ve already seen in
Beyond Light & Dark: Introducing a Balanced Theme System.
This means when you rotate the theme or dial saturation up/down, every semantic color that opts in can follow.
Plugging Terminol Into the Theme
Because Terminol is theme-based from the beginning, wiring it into this system was straightforward.
The terminal gets a TerminolTheme object:
const theme: TerminolTheme = {container: "bg-background text-foreground",line: "text-sm",prompt: "text-[var(--term-prompt)]",input: "text-foreground",success: "text-[var(--term-success)]",warning: "text-[var(--term-warning)]",error: "text-[var(--term-error)]",info: "text-[var(--term-info)]",muted: "text-muted-foreground",modal: "bg-background text-foreground",};
and then receives its dynamic semantic colors as CSS custom properties:
const dynamicColors = {"--term-success": getThemeColorValue("emerald"),"--term-warning": getThemeColorValue("amber"),"--term-error": getThemeColorValue("red"),"--term-info": getThemeColorValue("sky"),"--term-prompt": getThemeColorValue("stone"),"--term-input": getThemeColorValue("zinc"),} as React.CSSProperties;return (<div style={dynamicColors} className="h-full w-full contents">{/* Terminol lives here */}</div>);
Now, when the theme shifts, the terminal prompt, success/warning/error/info lines all shift with it, using the same OKLCH-based balance rules as the rest of the site. No extra configuration, no duplicated color logic.
Terminol doesn’t just sit on top of the theme system; it rides the same color mechanics that power the rest of KODKAFA.
What’s Next?
This update is mostly about wiring the existing theme system deeper into KODKAFA’s new intent-first, terminal-like UX, but there’s still work to do:
Extending the theme customizer so these terminal-specific semantic colors are visible and tunable from the UI.
Aligning more closely with the latest shadcn/ui theming guidelines:
The goal is still the same as when this journey started:
give users a more balanced, controllable light & color experience—now scaled up to a fully interactive, intent-driven surface at the heart of KODKAFA.