01 — HANORA / 2025

FROM PURPLE TO
CULTURAL CLARITY

How treating brand direction as a structured technical input prevented a visual identity crisis — and built an architecture where the purple→red pivot required exactly one file change.

ROLE
Frontend Engineer
YEAR
2025
STACK
Vue 3 + Nuxt 3 SSR
ANALYTICS
Plausible · GitLab CI/CD
01 — CONTEXT & PROBLEM

THE WRONG COLOUR FOR THE WRONG AUDIENCE

Hanora is an education consultancy connecting Russian-speaking students with Chinese universities, language programs, and international schools. This is a high-consideration service — users are making decisions about visas, relocation, and years of their academic lives.

The initial visual direction leaned toward purple. Purple currently carries strong associations with neon, cyberpunk, and modern tech aesthetics — a direct psychological mismatch with the service reality.

Users need to feel they are engaging with a scholarly cultural bridge, not a SaaS dashboard.
02 — VISUAL EVIDENCE

BEFORE & AFTER

Hanora before redesign — purple palette with SaaS-style aesthetics
BEFORE

Purple palette · SaaS visual language · Mismatched audience expectations

Hanora after redesign — warm off-white with refined red accent and Chinese typography
AFTER

Warm off-white · Scholarly red · Chinese typographic motifs · Institutional trust

03 — STRATEGIC FOUNDATION

BRAND.MD AS AN API CONTRACT

Before writing a single Vue component, we established a brand.md document — a single source of truth for the visual system, deliberately decoupled from implementation details.

It allowed the client to approve the emotional framework without needing to understand CSS. It allowed engineering to build against a stable interface. When identity required post-launch refinement, we changed variables — not components.

Warm off-white base (#FDFBF7)

Evokes paper, ink, and scrolls — not cold digital surfaces.

Establishes background token before any container logic.
Refined red accent (#B22222)

Replaced purple. Represents scholarly seals and traditional ink stamps.

Globally scoped CSS custom property — not hardcoded in components.
Large Chinese characters as structural motifs

Cultural authenticity and visual texture.

Semantic HTML text, not SVG. Demands specific a11y rules.
Serif typography (Noto Serif SC)

Scholarly, authoritative — distinct from typical sans-serif web.

Requires early loading and careful font-display strategy.
04 — ARCHITECTURE

VISUAL COMPONENTS AS STATELESS TOKEN RENDERERS

┌─────────────────────────────────────┐
│         brand.md  (SSOT)            │
│  ┌──────────────┐ ┌──────────────┐  │
│  │ Color Tokens │ │  Type Scale  │  │
│  └──────────────┘ └──────────────┘  │
└──────────────┬──────────────────────┘
               ▼
┌─────────────────────────────────────┐
│          Nuxt 3 Application         │
│  ┌─────────────┐ ┌───────────────┐  │
│  │ Theme Layer │ │  Composables  │  │
│  │ (CSS vars)  │→│ (Bus. Logic)  │  │
│  └─────────────┘ └───────────────┘  │
│  ┌─────────────────────────────────┐ │
│  │  Atomic Components              │ │
│  │  Consume CSS vars only —        │ │
│  │  no hardcoded colors            │ │
│  └─────────────────────────────────┘ │
└─────────────────────────────────────┘

The architecture separates concerns in a specific order. Design intent lives in brand.md. Token values live in CSS custom properties. Components consume tokens — never raw values.

This makes the architecture's contract explicit: any component can be authored in isolation, confident that the visual system is being respected at the token level — not through convention or discipline.

The purple → red pivot required exactly one line change. No component rewrites.
05 — IMPLEMENTATION

THREE KEY DECISIONS

A — TOKEN PROPAGATION

Design → Code in one hop

The brand.md colour palette is injected directly into the Nuxt runtime via CSS custom properties defined on :root. Every component references these tokens. The visual pivot required exactly one change in this file.

styles/tokens.css
:root {
  --brand-background: #FDFBF7;
  --brand-foreground: #1A1A1A;
  --brand-accent:     #B22222;
  --brand-accent-muted: #8B1A1A;
  --brand-border:     #E8E2D9;
}
B — CULTURAL MOTIFS

Design fidelity without sacrificing SEO

Large-format Chinese characters act as background motifs. Rather than embedding SVGs, they are rendered as semantic HTML text with opacity and position: absolute.

Googlebot sees the text. Screen readers see aria-hidden. Visual texture is preserved without clutter.

components/atoms/CulturalMotif.vue
<template>
  <span
    class="motif"
    :style="{ fontSize: `${size}rem` }"
    role="presentation"
    aria-hidden="true"
  >{{ character }}</span>
</template>

<style scoped>
.motif {
  font-family: "Noto Serif SC", serif;
  font-weight: 700;
  color: var(--brand-foreground);
  opacity: 0.06;
  position: absolute;
  pointer-events: none;
  user-select: none;
}
</style>
C — SSR STRATEGY

Performance and trust via request-time rendering

The service relies on Russian-language search traffic. SSR with Nuxt's useAsyncData fetches university data at request time — fast TTFB for organic search, fresh content for dynamic listings.

One-hour revalidation prevents stale content without the overhead of a full build pipeline for every university update.

composables/usePrograms.ts
// composables/usePrograms.ts
export const usePrograms = (category: string) => {
  return useAsyncData(
    `programs-${category}`,
    async () => $fetch(`/api/programs/${category}`),
    { staleTime: 60 * 60 * 1000, server: true }
  )
}
06 — TRADEOFFS

DECISIONS & THEIR COSTS

Native CSS over Tailwind

Needed pixel-perfect control over the "ink-scroll" atmosphere — letter-spacing, custom serif rendering.

Slower initial component development. Eliminated 40KB+ of unused utility classes from the bundle.

No UI component library

Prevented the "generic SaaS" look. Custom accordion and navigation had to match the scholarly typographic grid.

Required building interaction primitives from scratch. Gave exact control over motion curves (cubic-bezier(0.77, 0, 0.18, 1)).

SSR over SSG

University data changes weekly. Dynamic SSR ensures fresh content for Russian-language search traffic.

Higher server costs and increased build complexity.

Plausible over GA

No cookie banners. Aligned with the "trustworthy" visual tone the client needed to establish.

Less granular behavioral data for advanced funnel analysis.

07 — OUTCOMES

POST-LAUNCH RESULTS

+22%
Session duration

Users spent more time with program filters and application details

−16pp
Bounce rate

Landing page bounce dropped from 54% to 38% post-launch

1 line
Visual pivot cost

Purple → red required exactly one token change, zero component rewrites

08 — REFLECTION

THE SYSTEMS APPROACH TO DESIGN

The most valuable outcome wasn't about Vue or SSR — it was about treating brand direction as a structured technical input. brand.md functioned like an API contract. When the visual identity required refinement post-launch, we changed variables — not components.

This decoupling is the difference between a website that looks like a template and one that feels authored. The interface is evidence of both design thinking and engineering discipline, because the engineering architecture was designed to protect the design integrity.

07 — WHY THIS MATTERS

FOR FREELANCE / PROJECT CLIENTS

A brand pivot that costs one token change, not a rebuild — this is what 'the architecture is the trust model' means for a fixed-price engagement: fewer change requests turning into fewer invoices.

FOR FULL-TIME / HIRING TEAMS

+22% session duration and −16pp bounce rate came from treating brand direction as a structured technical input — the same discipline scales to a design system a whole team ships against.

NEXT STEP

LIKE WHAT YOU SEE? LET'S TALK ABOUT YOURS.