Case study
Moringa: an online fair for Brazilian makers
A marketplace with two sides. Buyers find a piece, pay and follow the order until it arrives. Makers list pieces and ship orders from one panel. Every piece carries the story of who made it.
The problem
People who make things by hand in Brazil, like ceramists, weavers and woodworkers, mostly sell through Instagram messages, WhatsApp and local fairs. It works, but it costs them time: the same questions again and again, photos sent one by one, and no clear view of who paid and what still needs to ship. Large marketplaces solve the logistics but hide the person behind the piece.
I wrote a product brief, two proto-personas (Dona Lúcia, a ceramist, and Marina, a buyer) and a backlog with acceptance criteria before designing any screen.
What I built
- Catalog with search and filters by region, material and price, shareable in the URL.
- Bag and Stripe Checkout in test mode, with card and Pix, confirmed by a signed webhook.
- Buyer area: orders, a timeline that updates without reloading, tracking codes and reviews.
- Maker area: a guided form to list a piece, edit or pause it, a sales chart with a table view, and orders to ship.
- Portuguese and English, light and dark theme, usable with a keyboard and a screen reader.
Decisions worth explaining
The server never charges a price the buyer did not see
The bag keeps the price from the moment a piece was added. When makers became able to edit prices, that could leave an old price on the pay button. Now the bag updates itself and says what changed, and the server refuses to create a payment if the prices differ.
Forms check on submit, then while typing, never on blur
An error that appears when a field loses focus pushes the button down while the person is clicking it, and the click misses. Validating on submit and then on each change, as GOV.UK recommends, avoids that.
Rate limits that hold on serverless
Sign-in, checkout and order lookup have limits stored in Postgres, so every server instance shares the same count. The IP is saved only as a salted hash, following the LGPD principle of collecting as little as possible. If the store fails, the request goes through: a database hiccup should not lock everyone out.
A strict Content Security Policy
Each request gets a new nonce, and only scripts carrying it, or loaded by them, can run. With the other headers, the live site scores A+ on Mozilla Observatory.
Quality, measured
Measured on the live site and on every pull request. Last checked on October 22, 2026.
| Area | Result | How it is checked |
|---|---|---|
| PageSpeed Insights | 100 · 100 · 100 · 100 | Performance, accessibility, best practices and SEO, mobile and desktop; Lighthouse CI on every pull request |
| Accessibility | 0 axe violations · WCAG 2.2 AA | axe on every page in both themes, keyboard and focus tests in Playwright |
| Security headers | Mozilla Observatory A+ | Scanner run on the live site |
| Vulnerability scan | OWASP ZAP: 0 high, 0 medium, 0 low | Baseline scan on every pull request |
| Tests | 179 unit and integration · 158 end-to-end | Vitest, PGlite (real Postgres in memory), Playwright on mobile and desktop |
The QA cycle
After the build, I switched hats and tested Moringa as a QA analyst would, following a test plan. All 15 Must and Should stories have test cases: 58 in total, 49 automated and run on every pull request.
An exploratory session found BUG-001, rated High: after a maker edited a price, the bag and the pay button could still show the old one. It was fixed with regression tests at three levels, unit, integration and end-to-end.
The same cycle made the pipeline faster: one build is now shared by the end-to-end, Lighthouse and ZAP jobs, and the end-to-end suite runs in two parallel shards. Test summary report.
What I learned
- Asking "why does this trust that number?" led from a harmless tampering test to a real bug.
- A new feature can make an old assumption false. New stories now get a short "what does this make untrue?" check.
- Writing down false alarms saves the next person the same hour of investigation.
Try it
No sign-up needed: each demo account is one click. Pay with the Stripe test card 4242 4242 4242 4242, any future date and any CVC. Open Moringa.