DEV Community

Cover image for How We Built Ekehi: Engineering a Women's Business Intelligence Platform in One Sprint
AJ
AJ

Posted on

How We Built Ekehi: Engineering a Women's Business Intelligence Platform in One Sprint

I want to tell you about one of the most satisfying weeks I've had as an engineer.

Okay, maybe the title was click bait, since we only built the landing page, but it is still a platform, nevertheless.

Twelve contributors. Three tracks. One sprint. Zero frameworks. A fully structured, token-based, responsive landing page — shipped for International Women's Day 2026.

This is the engineering story of Ekehi — what we built, how we coordinated it across three tracks (frontend, UI/UX, and backend), the architectural decisions I made as Engineering Lead, and what I learned from running a synchronised multi-track team build at this pace.


What Is Ekehi?

Ekehi is a business intelligence platform built for women entrepreneurs and women-led SMEs across Nigeria and Africa. It aggregates funding opportunities — grants, VCs, loans, government schemes, accelerators — alongside training programmes, mentorship networks, and business resources into a single searchable, filterable hub.

The platform was created as part of the Tabî Project by TEE Foundation for International Women's Day 2026.

The problem it solves is real: women-owned businesses across Africa experience what researchers call a "triple penalty" — limited access to capital, fewer connections to formal business networks, and low visibility in male-dominated investment ecosystems. When funding opportunities do exist, most women never hear about them. Ekehi fixes the information gap.

The tech stack:

Layer Technology
Frontend HTML, CSS, Vanilla JavaScript
Backend Node.js + Express
Database Supabase (PostgreSQL)
Auth Supaash Auth + JWT
Hosting Netlify (frontend), Railway (backend)

No React. No Vue. No build step. Deliberate choices — all of them.


The Team

Ekehi was built across three tracks working in parallel.

Frontend Track

Contributor GitHub
AJ (Engineering Lead) @AJ1732
Marion Braide @MarionBraide
Florence @Florence-code-hub
Okwuosa Oluchi @luchiiii
Victor Okoukoni @Okoukoni-Victor
Esther Orieji @first-afk
Pheonixai @Pheonixai

UI/UX Track

Contributor
Michael Babajide Boluwatife
Fisayo Rotibi
Osuji Wisdom

Backend Track

Contributor
Olusegun Adeleke
Sodiq Semiu

QA

Contributor
Gabriel Abubakar

The frontend sprint described in this article ran concurrently with the UI/UX team producing the Figma designs and the backend team architecting the API and database layer. Everything you see on the landing page was built directly from those Figma deliverables — not interpreted, not approximated. Pixel-faithful implementation was the standard.


My Role: Engineering Lead

I wore a lot of hats on this project. Before a single line of product code was written, my job was to ensure every contributor could move fast, independently, and without stepping on each other.

That meant three things: architecture, tooling, and issue writing.

1. Monorepo Architecture (PR #2)

The first thing I merged was the project skeleton — before anyone else had touched index.html.

Ekehi/
├── client/
│   ├── pages/          # HTML files
│   ├── css/
│   │   ├── base/       # Reset, tokens, typography
│   │   ├── components/ # Reusable component styles
│   │   └── pages/      # Page-level styles
│   ├── js/
│   │   ├── api/        # Fetch wrappers
│   │   ├── components/ # UI interaction logic
│   │   ├── pages/      # Page entry scripts
│   │   └── utils/      # Shared helpers
│   └── assets/
└── server/
    └── src/            # MVC: routes, controllers, models, middleware, services
Enter fullscreen mode Exit fullscreen mode

I also added the full contributor tooling before any work started:

  • CONTRIBUTING.md covering both frontend and backend workflows
  • Three GitHub issue templates (Bug Report, Feature Request, Task)
  • A PR template enforcing screenshots, linked issues, and a consistent checklist
  • .gitignore covering .env and node_modules
  • server/.env.example documenting every required environment variable

The principle here is simple: a team that can't onboard fast, can't ship fast. Every hour saved on "where does this file go?" is an hour spent building.


2. The Design System (PRs #26 + #31)

This is the work I'm most proud of on this project.

Before the team styled a single element, I converted the Figma design system into a complete CSS-native token system — no Sass, no PostCSS, no JS-in-CSS. Pure custom properties.

:root {
  /* Color primitives */
  --color-purple-800: #4c0066;
  --color-purple-600: #9900cc;
  --color-neutral-950: #131213;

  /* Semantic aliases */
  --color-primary:          var(--color-purple-800);
  --color-primary-hover:    var(--color-purple-600);
  --color-text-on-primary:  var(--color-white);
  --color-bg-base:          var(--color-white);

  /* Type scale */
  --font-size-base: 1rem;
  --text-xs:   calc(var(--font-size-base) * 0.75);   /* 12px */
  --text-sm:   calc(var(--font-size-base) * 0.875);  /* 14px */
  --text-base: var(--font-size-base);                /* 16px */
  --text-5xl:  calc(var(--font-size-base) * 3);      /* 48px */

  /* Spacing */
  --spacing: 0.25rem;
  --space-4:  calc(var(--spacing) * 4);   /* 16px */
  --space-8:  calc(var(--spacing) * 8);   /* 32px */
  --space-16: calc(var(--spacing) * 16);  /* 64px */
}
Enter fullscreen mode Exit fullscreen mode

Two layers — primitives and semantic aliases. The primitives (--color-purple-800) give you the raw value. The aliases (--color-primary) give you the intent. This separation means you can retheme the entire product by changing three or four semantic aliases without touching a single component.

The rule I enforced for every contributor: no hardcoded values, anywhere. No #4c0066 in a component file. No font-family: 'Urbanist' in a section stylesheet. Every value references a token.

I also built the button component — the first real piece of UI. It uses local CSS custom properties to create a self-contained state machine:

.btn {
  --btn-bg:           transparent;
  --btn-color:        var(--color-text-primary);
  --btn-bg-hover:     transparent;
  --btn-color-hover:  var(--color-text-primary);

  background-color: var(--btn-bg);
  color:            var(--btn-color);
  transition: background-color 150ms ease, color 150ms ease;
}

.btn:hover {
  background-color: var(--btn-bg-hover);
  color:            var(--btn-color-hover);
}

.btn--primary {
  --btn-bg:           var(--color-primary);
  --btn-color:        var(--color-white);
  --btn-bg-hover:     var(--color-primary-hover);
  --btn-color-hover:  var(--color-white);
}
Enter fullscreen mode Exit fullscreen mode

The modifier (--primary) only sets local properties. It never writes background-color or color directly. The base .btn rule is the only rule that does. This means specificity stays flat — modifiers can't fight each other.

One file per section was the other structural decision I made in PR #31. Every BEM block lives in its own scoped file:

css/pages/landing/
├── nav.css
├── hero.css
├── about.css
├── value-proposition.css
├── mission.css
├── what-we-offer.css
├── cta.css
└── footer.css
Enter fullscreen mode Exit fullscreen mode

landing.css imports them in document order. The result: six developers styling six different sections simultaneously with zero merge conflicts on CSS files.


3. Issue Writing as Engineering Work

This part often goes undocumented. I want to talk about it.

Every issue I wrote before the sprint started had:

  • A screenshot from Figma showing exactly what to build
  • A full content specification (copy, heading levels, BEM class names)
  • An explicit list of dependencies (which issue must merge first)
  • A numbered acceptance criteria checklist
  • Implementation notes covering tricky edge cases

Here is an excerpt from the mobile nav toggle issue (#38):

On outside click:
If the menu is open and the user clicks anywhere outside .nav__inner, close the menu and reset all states.

On window resize:
If the viewport width exceeds the mobile breakpoint and the menu is open, close the menu and reset all states (prevents a stuck-open menu if the user resizes from mobile to desktop).

Implementation Notes:
Use aria-expanded as the single source of truth for open/closed state. Mobile breakpoint for the resize listener should match the CSS breakpoint — extract it as a named constant at the top of the script (const MOBILE_BREAKPOINT = 768).

That's not a vague ticket — it's a spec. The developer assigned to it, @MarionBraide, shipped the implementation in a single PR with every acceptance criterion met.

Well-written issues are one of the highest-leverage activities an engineering lead can do. They eliminate back-and-forth, reduce PR revision cycles, and let contributors focus on writing code instead of inferring intent.


4. Contributors Page (PR #44)

My final individual contribution was the contributors page — HTML structure, JavaScript renderer, and full CSS.

The architectural choice I want to highlight here is the JS rendering approach:

const contributors = [
  {
    name: "AJ",
    role: "Engineering Lead",
    image: "../assets/images/contributors/aj.jpg",
    imageStyle: { objectPosition: "top center" }
  },
  // ...
];

function renderContributors(data, containerId) {
  const container = document.getElementById(containerId);
  if (!container) return;

  const fragment = document.createDocumentFragment();

  data.forEach((contributor, index) => {
    const card = document.createElement("article");
    card.className = "contributor-card";
    card.dataset.variant = String((index % 5) + 1);
    // ...build card elements...
    fragment.appendChild(card);
  });

  container.appendChild(fragment);
}

document.addEventListener("DOMContentLoaded", () => {
  renderContributors(contributors, "contributors-grid");
});
Enter fullscreen mode Exit fullscreen mode

Three things worth noting:

  1. DocumentFragment batches DOM writes — one reflow instead of N. For a grid of cards, this matters.
  2. createElement over innerHTML — XSS-safe by construction. No string interpolation, no injection surface.
  3. Adding a contributor requires only a new data entry — no HTML changes, no render function edits. The imageStyle field lets each contributor's photo be positioned correctly without touching CSS.

How We Coordinated Seven People in One Sprint

The team was fully distributed. We had no standup calls during the build phase. What we had instead was process.

GitHub Issues as the Source of Truth

Every task existed as a GitHub Issue before work started. Contributors picked up issues, created branches named after them, and opened PRs that closed the linked issue with Closes #N. This kept the board clean and made progress visible in real time.

Dual Workflow: ClickUp + GitHub

Internal contributors tracked tasks in ClickUp (moving cards from In Progress to Done as PRs merged). External contributors worked entirely through GitHub Issues. Both systems pointed at the same work, so nothing fell through the gap.

PR Template Enforcement

Every PR had to include:

  • A description of what changed and why
  • Screenshots for any UI change
  • Closes #N linking the issue
  • A checklist: branch up to date, no .env committed, mobile tested

PRs without all of this were held for revision. This sounds strict — and it is. It also means I never had to ask "what does this PR do?" when reviewing.

Branching Strategy

main
└── development  ← all PRs target here
    ├── feature/*
    ├── fix/*
    └── chore/*
Enter fullscreen mode Exit fullscreen mode

No one committed to main or development directly. Every piece of work lived on a branch. This protected the integration branch and meant we could always roll back a section without touching others.


What the Team Shipped

In the order they merged:

# PR Contributor
#2 Monorepo architecture + contributor tooling @aj1732
#26 Design system — CSS tokens + utilities @aj1732
#31 Button component + section CSS scaffold @aj1732
#15 About section HTML @Florence-code-hub
#17 Navigation HTML @MarionBraide
#19 Footer HTML @Pheonixai
#20 Mission section HTML @luchiiii
#23 What We Offer section HTML @MarionBraide
#24 Value Proposition section HTML @Okoukoni-Victor
#32 CTA section HTML @Florence-code-hub
#34 Hero section HTML @first-afk
#35 Mission eyebrow image assets @luchiiii
#36 Navigation CSS @MarionBraide
#37 About section CSS @Florence-code-hub
#39 What We Offer section CSS @MarionBraide
#40 Hero section CSS @first-afk
#42 Value Proposition section CSS @Okoukoni-Victor
#43 Mobile navigation toggle JS @MarionBraide
#44 Contributors page (HTML + JS + CSS) @aj1732

Lessons Learned

1. The architecture investment pays back immediately

I spent the first hours of the sprint on things that produced no visible UI — folder structure, token system, CSS architecture, issue templates. Every hour I spent there saved the team two. When six people are styling six sections simultaneously, they are not stepping on each other because of architecture decisions made before they wrote their first rule.

If you're leading a multi-contributor frontend project: set up the system before you invite contributors in.

2. Issue quality is a force multiplier

The difference between "style the navigation" and a four-paragraph spec with Figma screenshots, BEM class names, acceptance criteria, and implementation notes is the difference between three revision cycles and one. Your time writing the issue is paid back in review time saved.

3. CSS custom properties are a full design system

I did not reach for Sass. I did not reach for Tailwind. CSS custom properties with a thoughtful primitive/semantic layer give you everything you need for a design system — theming, inheritance, component-level overrides, responsive tokens — in native CSS. The DX is excellent once contributors understand the naming convention.

4. Vanilla JS is underrated for constrained scopes

The contributors page renderer, the mobile nav toggle, the offerings tab interaction — all written in plain JavaScript. No bundler, no framework, no dependency to maintain. The frontend opens directly in a browser. The bundle size is zero because there is no bundle. For a project at this stage, that is the right call.

5. One file per section is not over-engineering

It sounds like premature organisation. It isn't. When you have seven contributors and eight CSS sections, one file per section means eight people can make CSS changes in the same branch with zero merge conflicts. The cost is eight @import lines in landing.css. That's a good trade.


What's Next for Ekehi

The landing page sprint was phase one. What comes next:

  • Mission and CTA section CSS — two open issues being finished by the frontend track
  • Backend API — Olusegun and Sodiq are building the Express + Supabase layer for the funding opportunity database
  • Search and filter — full-text search with sector, stage, location, and funding size filters
  • Opportunity submission system — funder-facing form with an admin review workflow
  • Authentication — Supabase Auth + JWT for registered users
  • QA integration — Gabriel's QA process formalised as acceptance test criteria against each API endpoint and UI flow

The codebase is open source. If you're a developer who wants to contribute to a project with real-world impact for women entrepreneurs across Africa — the issues are open, the CONTRIBUTING.md is thorough, and the bar for a good PR is clearly documented.

GitHub: github.com/Tabi-Project/Ekehi


Shoutout to the Team

None of this ships without every one of these people.

Frontend Track

  • @MarionBraide — Navigation HTML and CSS, What We Offer section, mobile nav JavaScript. Marion owns the most technically complex section on the page and delivered a clean, accessible, BEM-compliant implementation with a working JS interaction layer across four separate PRs.

  • @Florence-code-hub — About section HTML and CSS, CTA section HTML. Florence nailed the mixed serif/sans heading treatment that gives Ekehi its distinct visual identity.

  • @luchiiii (Okwuosa Oluchi) — Mission section HTML and the SVG decoration assets for the mission eyebrow. Oluchi also spotted and fixed missing <img> tags in a follow-up PR — exactly the attention to detail that keeps a shared codebase clean.

  • @Okoukoni-Victor (Victor Okoukoni) — Value Proposition section, HTML structure and CSS. Victor's responsive two-column layout with the visual block and caption section was clean from the first commit.

  • @first-afk (Esther Orieji) — Hero section HTML and CSS. Esther set the visual tone of the page with the wide-aspect hero display block that grounds the entire landing page.

  • @Pheonixai — Footer HTML. The footer is one of the most content-dense sections on the page — three navigation groups, a brand block, and a legal bottom bar — structured cleanly in a single PR.

UI/UX Track

The frontend sprint could only move at the speed it did because the designs were ready. Every section that got built had a Figma frame to implement against — with precise layout specs, color tokens, type scales, and component states defined before a single HTML file was touched.

  • Michael Babajide Boluwatife — Core product design and design system foundation. The purple scale, the serif/sans heading pairing, the token naming convention — these design decisions shaped the CSS architecture built.

  • Fisayo Rotibi — Component and section design. Fisayo's contributor card designs directly informed the card rendering system, including the five SVG background variants and the per-card image positioning approach.

Working from a well-produced Figma file is a gift to a frontend engineer. You're not guessing. You're translating.

Backend Track

While the frontend sprint was running, the backend team was laying the foundation for what Ekehi will become once the product goes live.

  • Olusegun Adeleke — API architecture and database schema design. The Express MVC structure I scaffolded (routes → controllers → models → services) was designed to directly receive Olusegun's implementations.

  • Sodiq Semiu — Backend development and Supabase integration. The server/.env.example I included in the initial monorepo commit documents the Supabase keys and connection strings that Sodiq's work depends on.

The separation of client/ and server/ in the monorepo was intentional — frontend and backend contributors never needed to be in each other's directories. Both tracks moved independently on the same codebase.

QA

  • Gabriel Abubakar — Quality assurance. Gabriel was the safety net across all three tracks — verifying that implementations matched the design specs, that mobile breakpoints held, and that interactions behaved as the issues defined. QA on a multi-track team is often underacknowledged. Without it, bugs that feel "done" in development show up in production.

This project matters. Women entrepreneurs in Africa are funding their businesses with imperfect information, or no information at all. Ekehi is a small piece of the solution to that — and it was built by a team of developers who showed up, picked up issues, and shipped.

That's the kind of work worth documenting.


Built for International Women's Day 2026 as part of the Tabî Project by TEE Foundation.
Open source — contributions welcome.

Top comments (0)