Mikono Creations
A full store for a Nairobi craft business that supports 25+ women: 30 animals, a custom-order studio with 29 order types, gift and size finders, a budgeted animal layer, and a checkout that writes a referenced WhatsApp order sized to fit the link.
- 51route templates
- 29custom order types
- 88/88supplied photos and video used
- 294unit tests passing
The problem
- The brief arrived as 5 WhatsApp zips: 87 photos, 1 video, a logo, seven stockists and a founder story. Prices, delivery fees and centimetre sizes were not supplied.
- The client ruled out bright colours in the interface for a children's brand, then asked for a site that felt more alive.
- Kenyan customers buy through WhatsApp, not card checkout, and much of the work is custom: mascots, corporate gifts, pet lookalikes, classroom sets.
Architecture
Key decisions
Two real prototypes before choosing a look
A dark glass direction and a daylight clay direction were both built as static pages with the real photos and screenshotted at phone and desktop sizes.
No invented prices
One pricesConfirmed flag drives four price states, so no total is shown or estimated until every line and the delivery fee are known. The catalogue throws at build time if the flag is on and any product lacks a price.
Motion with a budget
An inline head script picks the animal tier before first paint from the visitor's switch, reduced motion, Save-Data and device memory. The engine then lets at most 4 animals move on a phone, 6 on desktop, 2 on weak devices.
Pre-built images instead of a runtime optimiser
The hosted image optimiser hit its plan limit, so every photo is now resized once at build time and served as static WebP.
Real code
export const URL_BUDGET = 2000;
// …
/** Picks the longest level whose encoded URL fits the budget. fullText is always the full message for copying. */
export function planOrderSend(number: string | undefined, o: OrderMsg): SendPlan {
const fullText = buildOrderMessage(o, "full");
const levels: Level[] = ["full", "compact", "short"];
let last: SendPlan | null = null;
for (const level of levels) {
const text = level === "full" ? fullText : buildOrderMessage(o, level);
const url = buildWaUrl(number, text);
last = { level, text, fullText, url, tooLong: false, pasteRest: level !== "full" };
if (!url || url.length <= URL_BUDGET) return last;
}
return { ...(last as SendPlan), tooLong: true };
}function setBudget() {
const phone = innerWidth < 900;
budget = tier === "lite" ? 2 : phone ? 4 : 6;
try { if (sessionStorage.getItem("mk-fx-slow")) budget = Math.max(1, budget - 2); } catch { /* storage blocked */ }
}
// …
if (!force && lastT && dt > 100 && motionOK()) {
slowFrames = slowFrames.filter((t) => now - t < 2000);
slowFrames.push(now);
if (slowFrames.length >= 3) { try { sessionStorage.setItem("mk-fx-slow", "1"); } catch { /* storage blocked */ } setBudget(); soon(0); slowFrames = []; }
}export const wantsBusiness = (b: Brief) => isBusinessCustomer(b) || hasType(b, (t) => !!t.business);
// …
export const isBulk = (b: Brief) => hasType(b, (t) => !!t.bulk) || b.pieces.some((p) => !!qtyBands.find((x) => x.id === p.qtyBand)?.bulk) || totalCount(b) >= BULK_FROM;
// …
export const mayHaveLogo = (b: Brief) => hasLogoFinish(b) || hasType(b, (t) => !!t.logo) || b.pieces.some((p) => p.baseId === "character");
// …
export function activeSteps(path: PathMode, b: Brief): StepId[] {
if (path === "quick") return quickSteps;
return fullSteps.filter((s) => s !== "business" || wantsBusiness(b));
}t("the KRA PIN is in the message but never in a draft", () => {
const form = { ...F.emptyForm, customerTypes: ["shop"], name: "Amina", phone: "0712345678", businessName: "Savanna Gifts", kraPin: "A123456789B", paymentNote: "x" };
const ticks = { ...F.noTicks, terms_acknowledged: true };
const msg = F.toOrderMsg(form, [{ sku: "a-b-s", name: "A", colourLabel: "B", size: "S", qty: 1 }], "MK-261001-AAAA", ticks, "", "", C.CONSENT_VERSION);
assert.ok(W.buildOrderMessage(msg, "full").includes("KRA PIN: A123456789B"));
const full = W.buildOrderMessage(msg, "full");
F.writeDraft({ step: "review", skipGift: false, skipAbout: false, form, ref: "MK-261001-AAAA", sent: { message: full, text: full, level: "full", at: 1 } });
const raw = mem.get(F.DRAFT_KEY);
assert.ok(raw && !raw.includes("A123456789B") && !raw.includes("kraPin"), "draft has no PIN");
const back = F.readDraft();
assert.equal(back.form.kraPin, "");
});How it was verified
- Three independent verifiers (visual, content and media, function and accessibility) per rendering phase; their reports for the shop, trade and content phases are committed to the repo.
- 294 unit tests across checkout, WhatsApp messages, enquiries, the studio and helpers, including privacy rules: no child data fields, and the KRA PIN never stored on the device.
- Build gates: a copy scan for dashes and banned phrases, a choreography check for the animal layer, and an OKLCH chroma cap of 0.13 with WCAG AA contrast pairs.
- Playwright mobile scripts for layout, interactions, width and performance across 11 viewports; the width gate passes all 366 page and width runs.
Results
- All 88 supplied files (87 photos, 1 video) appear on the site; the 44 unclear species were settled by vote for the client to confirm at review.
- Live with 51 route templates, 30 animals, 47 journal posts and a custom studio that turns any brief into one WhatsApp message.
- The pipeline was written up as the Foundation Build Playbook for future client sites.
What I'd do next
- Switch on prices and product offer markup once the client confirms them.
- Clear the open critical and high findings from the latest mobile layout gate.
- Run the SEO crawler against the live site and fix what it finds.
- Have a Kenyan advocate review the 16 draft legal documents before launch.

