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.
Purple palette · SaaS visual language · Mismatched audience expectations
Warm off-white · Scholarly red · Chinese typographic motifs · Institutional trust
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.
Evokes paper, ink, and scrolls — not cold digital surfaces.
Replaced purple. Represents scholarly seals and traditional ink stamps.
Cultural authenticity and visual texture.
Scholarly, authoritative — distinct from typical sans-serif web.
┌─────────────────────────────────────┐
│ 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 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.
:root {
--brand-background: #FDFBF7;
--brand-foreground: #1A1A1A;
--brand-accent: #B22222;
--brand-accent-muted: #8B1A1A;
--brand-border: #E8E2D9;
}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.
<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>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
export const usePrograms = (category: string) => {
return useAsyncData(
`programs-${category}`,
async () => $fetch(`/api/programs/${category}`),
{ staleTime: 60 * 60 * 1000, server: true }
)
}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.
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)).
University data changes weekly. Dynamic SSR ensures fresh content for Russian-language search traffic.
Higher server costs and increased build complexity.
No cookie banners. Aligned with the "trustworthy" visual tone the client needed to establish.
Less granular behavioral data for advanced funnel analysis.
Users spent more time with program filters and application details
Landing page bounce dropped from 54% to 38% post-launch
Purple → red required exactly one token change, zero component rewrites
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.
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.