Skip to content

COROS AI · Case Study

COROS AI Redesign: MUI → shadcn

AI life coaching app. I rebuilt the entire product UI and its design system, live on web, iOS, and Android.

Design SystemsUX/UIResponsiveDesign Engineering
Role
Product Designer
Ownership
Design system owner
Platforms
4 platforms: web, tablet, iOS, Android

The product

COROS AI: an AI life coach that helps you navigate relationships and communication.

It is a B2C AI-native coaching platform with a chat-based interface, where people have long, ongoing conversations by text and voice. I joined as the founding product designer, and my work has spanned product design, UX, prompt engineering, user research, and brand.

Try what I built

The live product, in light and dark

At a glance

54+

semantic tokens, light and dark

Days → hours

design-to-review cycles

4

platforms: web, tablet, iOS, Android

Context

The problem

Testers said the v1 product strained their eyes. Engineering was moving to shadcn, and I owned the design side of the migration.

Foundation

Design system

One rule: components never touch raw colors. Everything resolves through primitives → brand layer → semantic tokens, mirroring the code exactly.

Raw primitives → Brand layer → Semantic tokens → Components

Figma variables editor showing the semantic colors collection with a shadcn (light) column and a shadcn-dark column, each token resolving to a brand-neutrals, brand-shades, coros-green, or coros-red reference.
Figma variables editor showing the typography collection: font definitions for sans, serif, headings, body, and monospace, plus heading scales with weight, size, line-height, and letter-spacing tokens.

The system in Figma: semantic colors holding a value per token for both light and dark modes, and typography resolving through shared font definitions.

  • Custom 11-stop scales for COROS blue, orange, and blue-tinted neutrals.
  • 54+ tokens across light and dark via Figma variable modes: design once, toggle themes with one click.
  • Published as a shared library; the whole team builds on it without detaching.

Architecture

Components

Variants only when structure changes. Everything else is a property. The chat input handles 4 states × all breakpoints without a variant explosion.

Screens

Onboarding

8 dark-only screens → 6 themed screens, name moved up front so the flow addresses you personally.

Welcome through Loading on web (dark theme), including the greeting that rotates through nine languages and the tone selection.

The tone screen teaches by interaction: pick Supportive or Provocative and the orb, card, and background respond.

Mobile tone selection with Supportive and Provocative cards, each carrying an animated orb.
Mobile dimensions selection with seven pill options, several selected.

The same flow on mobile (light theme): the multilingual welcome, the tone cards with their orbs, and the dimension pills. Every screen was designed for web, tablet, iOS, and Android at once.

Screens

Chat

Designed the full state inventory: empty, streaming, errors, message actions, return-to-chat. Prototyped 3 user-bubble options in working HTML to settle the debate.

Chat on web (dark theme): the greeting, a streamed reply, and the message actions beneath each COROS response.
Mobile chat in light mode showing date dividers and read-aloud and flag actions beneath a COROS message.
Mobile chat with an inline tone quick-switch popover offering Supportive, Provocative, and More Personalization.

The same surface on mobile (light theme): date-grouped history with message actions, the inline tone quick-switch, and composing a message. One component system, both themes.

Every state a conversation can be in

Chat is the product, so the states got inventoried before any screen was drawn. Most of them are waiting or failure states, not the happy path: empty, streaming, errors, message actions, return-to-chat.

Empty stateTriggered byA new conversation with no messages yet.User seesThe greeting and the composer, with nothing above it.
Typing indicatorTriggered byThe message has been sent and the model has not begun responding.User seesAn indicator in place of the response, so the wait is accounted for.
Streaming textTriggered byThe response has started arriving.User seesText building in the response bubble, distinct from the typing state.
Message-level actionsTriggered byA COROS response has completed.User seesRead-aloud and flag actions beneath the message.
Error and retryTriggered byThe response fails.User seesThe failure surfaced in place, with a retry available.
Scrolled away mid-responseTriggered byThe user scrolls up while a response is arriving.User seesA return-to-chat affordance rather than a forced scroll jump.

Screens

Settings

Modal shell with persistent nav. Appearance toggle moved into the sidebar so switching themes never requires navigating.

The settings modal shell on web (light theme): a persistent left nav that never jumps between tabs.
Personalization on web (light theme): tone, dimensions, and influences share the same modal shell.
Mobile sidebar flyout with search chats, send feedback, and the user profile pinned at the bottom.
Mobile settings modal showing the profile header and the Account group.
Mobile settings with Connected accounts expanded inline, listing Google, LinkedIn, Microsoft, and Apple.

The settings shell on mobile: the sidebar flyout, the profile and Account group, and Connected accounts expanding inline rather than pushing to a new screen.

Mobile influences search with a live results dropdown of historical and cultural figures.
Mobile influences with selected chips: Martin Heidegger, Barbie Doll, and Brené Brown.
Mobile delete-account confirmation dialog requiring the user to type delete, its confirm button on the destructive token.

States, not just screens: searching influences, the selected chips, and the type-to-confirm delete dialog on its own destructive token.

Feature

Retrieved Context panel (team-only)

My favorite piece. Debugging the AI meant engineers digging through logs. I designed an in-product panel that shows exactly what the model saw for any response:

When a response lands wrong, the first thing to establish is whether the system knew which conversation it was in. The new-topic-versus-continuation field answers that directly. A correct answer to the wrong topic still reads to the user as a broken conversation.

  • Retrieved sessions with semantic and recency scores.
  • Session-boundary probability.
  • Chunk results.
  • Memory.

I did prompt QA myself, so I designed it as my own user. Every field answers a question I actually had.

The team-only My Memories tab in settings, showing Biographical memory and Session History rows in the same modal shell as every user-facing tab.
My Memories (team-only): biographical memory and session history, in the same settings shell as every user-facing tab.

One system, two voices

User

I feel like I'm working overtime every single day, but my team just keeps giving me grunt work and I'm so pissed off.

Supportive

I hear you. How are you doing as you bring this up? What's happening at work?

Provocative

Are you going to do it or not?

Tone selection swaps the whole response architecture, not a word bank, which is why one response asks for more context and the other asks for a decision. The inline tone quick-switch shown above lets a user change it mid-conversation.

Impact

Results

  • Live in production on 4 platforms at app.coros.ai.
  • Zero redesign requests on documented handoff specs.
  • A library the whole team ships from.