// 004  ·  Product Design

Fior: Water Truth

fíor  ·  Irish  ·  true, real, pure

Product Design UX Research Design Systems IoT Health Project Management Prototyping with AI

A mobile app paired with RYAKKA, a bespoke IoT sensor that reads six drinking-water parameters at home and collapses them into four plain verdicts: Excellent, Good, Medium, Bad. Designed for an Irish context where the official reports are annual PDFs and compliance is reported as averages, but the person at the tap still has no idea what came out today.

Role Researcher & UX Designer
Timeline Jun 2025 - Paused
Team Solo
Platform iOS · Android · IoT

// Problem

497K
People on at-risk supplies
EPA Remedial Action List · 2024
99.8%
National compliance
EPA · Public Water Supplies

// Response

6
Sensor parameters
RYAKKA · TDS · TOC · COD · UV275 · EC · TEMP
4
Verdict states
Excellent · Good · Medium · Bad

What I noticed

In Ireland, an annual EPA compliance report still outpaces what the person at the tap can confirm today. Surface waters slip: only 52% of surface waters are in satisfactory ecological condition, down from 54%. The lead limit halves in 2036 (10 → 5 µg/L), with the highest risk on infants, children, and pregnant women (EPA, 2025). Headline compliance reads reassuring on paper. The kettle disagrees.

What I built

Fior is a mobile companion to RYAKKA, a home sensor that pairs six readings with a four-state Verdict Ladder. The language is calm, not alarmist; every claim is sourced, not asserted. What you can do next, not what you should fear.

What it taught me

Conceptual MVP, not shipped. Research drove a persona pivot from rural-first to urban-renter for go-to-market. The output: a token-first design system (294 variables across three layers, as of v3.2), 15 navigable hi-fi screens, and an ecosystem of three coordinated surfaces (case study, process doc, hi-fi prototype) sharing one design system.

// 01

The Research That Shaped Fior

I moved to Ireland in 2022. The first thing my kettle taught me was what limescale looks like: a white crust on the heating element after a week, no matter how often I rinsed it. The tap water was technically safe; the EPA said so. I still didn't really know what was in it.

That gap, between “water is technically safe on the national average” and “I, today, in my house, can confirm it”, became the brief. The consumer category barely exists in Ireland: EPA data sits in institutional reports, not in people's pockets. Fior closes the distance.

// Evidence Base · EPA 2025
  • 99.8%+ of Irish public water samples comply with bacterial and chemical limits, but that national average masks localised risk.
  • ~497,000 people remain on “at-risk” supplies (EPA Remedial Action List, end-2024).
  • The lead limit halves in 2036 (10 → 5 µg/L). Highest risk sits with infants, children, and pregnant women. EPA flagged national progress as “far too slow” (Jul 2025).
  • Surface water quality is declining: only 52% of surface waters are in satisfactory ecological condition, down from 54%.

Sources: EPA · Drinking Water Quality 2024 · EPA · Water Quality Report 2019–2024

Step 01
01

Impact Mapping

Broad Blast Radius · Four Impact Categories

Started broad, on purpose. The prompt was “what are the impacts of untreated water in Ireland?”, not “what app should we build?”. Mapping the blast radius first stops a project from collapsing into a single feature before the problem is understood.

  • Health: waterborne illness, lead exposure, long-term public health
  • Drinking water quality: compliance gaps, private wells, monitoring blind spots
  • Environment: surface water decline, agricultural runoff, estuarine damage
  • Economy: healthcare cost, bottled-water dependency, tourism risk

Each category unlocked a different angle for the next round of questions.

Step 02
02

Problem Statement & Persona Pivot

Statement Held · Primary Persona Shifted

First cut of the problem statement: “Rural households in Ireland need a reliable and affordable way to access clean and safe water, because untreated water can cause health, environmental, and economic issues.”

That statement survived until personas were sketched, and then it pivoted.

// Tension · Persona Pivot

The sharpest primary persona turned out to be the Concerned Renter (urban, 25–40, Dublin / Cork / Galway). Rural well owners have higher objective risk, but renters have higher engageable risk: they can't modify plumbing, they suspect lead pipes, they already buy filtered jugs, and they live inside the quantified-self product category.

Rural well owners became the secondary persona; new parents the tertiary. The problem statement held. The primary target shifted from who was most at risk to who was most likely to act.

Step 03
03

How Might We: 8 Directions

Diverge · One Angle per Impact Vector

Eight How-Might-We prompts, one per impact vector. The aim was not to pick a winner here. It was to surface angles so the ideation round wouldn't start pre-committed.

  • Access… make water testing available to anyone, anywhere in Ireland?
  • Education… turn EPA data into something people actually read?
  • Monitoring… give households a way to check their water continuously?
  • Community… aggregate local readings into a shared map?
  • Tech Leverage… use sensors and phones to move the cost floor down?
  • Feedback Loops… show change over time, not just a snapshot?
  • Service… connect every reading to a next action (boil, filter, call)?
  • Incentives… reward contribution to the community data set?
Step 04
04

Ideation: Four Product Directions

Converge · Filter on Daily Value + Moat + Scope

The HMWs collapsed into four plausible products. Each was scored against three filters: personal daily value, defensibility, and scope discipline (could a solo designer prototype it end-to-end?).

  • 01 Map of clean water sources: directory play, weak personal loop. Rejected
  • 02 Education app: content-led, hard to sustain, no moat. Rejected
  • 03 Monitor + Improve: daily personal value, hardware moat via RYAKKA, contains scope. ★ Selected
  • 04 Community treatment hub: operational product, far out of scope for this brief. Rejected
// Why Direction 03

Personal value every single day: you don't check a map every day, but you do drink water every day. A hardware moat through RYAKKA, the companion IoT sensor that turns six parameters into one verdict. And a scope a solo designer can actually deliver end-to-end: six readings, one verdict, one app. Clean boundary.

Step 05
05

First Wireframe: The Spine

4-Screen ASCII · Ancestor of the 15-Screen Hi-Fi

Before any pixel, a four-screen ASCII sketch. Home → Test → Result → Suggestions. That spine is still visible in the 15-screen hi-fi prototype: Home became Dashboard, Test became Measuring, Result became the verdict screen, Suggestions stayed as a hub.

┌────────────────┐   ┌────────────────┐   ┌────────────────┐   ┌────────────────┐
│   HOME         │   │   TEST         │   │   RESULT       │   │  SUGGESTIONS   │
│                │   │                │   │                │   │                │
│  Last test:    │ → │  [ Dip RYAKKA] │ → │   GOOD · 76    │ → │  1. Boil 3 min │
│  08:42 · Good  │   │   measuring…   │   │   TDS · TOC    │   │  2. Filter jug │
│                │   │    ◉ ◉ ◉       │   │   COD · UV275  │   │  3. Log source │
│  [Test again]  │   │                │   │   EC  · TEMP   │   │                │
└────────────────┘   └────────────────┘   └────────────────┘   └────────────────┘
// 4-screen sketch · direct ancestor of the 15-screen hi-fi ← Scroll horizontally to see the full flow →

Everything that followed (a three-layer token system, five pillar colours, the Verdict Ladder component, the Inter → Nunito migration) was thickening the spine. The spine itself never moved.

// 02

A Token-First Foundation

Fior's design system lives in Figma and in code: a Figma library built on variables, components and auto layout, and the token layers implemented in code. Three layers of tokens, a typography migration mid-project, and a single four-state component that carries the whole product's point of view on health and honesty.

294
Variables
27
Screens in Figma
0
Hardcoded Hex

// Figma library · v3.2 · Oct 2026

Principle 01
01

Three-Layer Token Architecture

Primitives → Semantic → Component

Every token in the system sits in one of three layers. Primitives hold raw values. Semantic tokens map meaning onto those values (primary, surface, danger). Component tokens bind the semantic layer to a specific UI part. Swap a primitive, the upper layers update automatically.

01 · Primitive --blue-500 #1A70B4
→
02 · Semantic --accent = --blue-500
→
03 · Component --button-primary-bg = --accent
  • 294 variables across the three layers in Figma (94 primitive · 98 semantic · 102 component), every one with an explicit scope
  • 4,450+ variable bindings on authored layers across the 27 Figma screens
  • Zero hardcoded hex in the prototype’s CSS modules and across the 27 Figma screens: every value traces up through semantic to primitive
Principle 02
02

Inter → Nunito Migration

One Primitive Swap · In Code

Mid-project the brand tone broke the typeface. Inter is crisp and neutral: right for SaaS, wrong for a calm Irish utility that wants to feel like a knowledgeable friend. Nunito's rounded terminals land warmer without surrendering legibility.

// Why the Architecture Paid Off

With a flat system this migration would have been a search-and-replace across every text style. With the three-layer architecture, the change in code was a single primitive swap: --font-family-base pointed from Inter to Nunito, and every semantic and component token propagated automatically.

Ten minutes of work, zero visual regressions. The architecture wasn't theoretical. It paid back the cost of setting it up on the first structural change.

The Figma file was slower to follow: it only left Inter in October 2026. That story is chapter 04.

Principle 03
03

The Verdict Ladder

Four States · One Component · One Point of View

Six sensor parameters (TDS, TOC, COD, UV275, EC, TEMP) are complicated. A verdict is not. The Verdict Ladder is the single component that turns the reading into a headline anyone can act on in under two seconds. Four states. Plain English. No numeric wall.

State 01
Excellent
#1AC863 · Green
State 02
Good
#2B8FD9 · Blue
State 03
Medium
#FFB200 · Yellow
State 04
Bad
#F43535 · Red
// Key Decision · Verdict Before Parameter

The dashboard leads with the verdict, not the readings. COD, TDS, UV275 are evidence. They live one tap deeper for the user who wants them. Most users don't. Calm tone over scary tone: the four states never say “unsafe” or “warning”. They say what you should do next (boil, filter, drink).

// Visual Language

Tokens, Type & Spacing

The five pillar colours and their supporting hues, the Nunito type scale, and the base-4 spacing grid: the surfaces the three-layer architecture ultimately resolves to.

// Five Pillars, Neutral & Support · Working Step Shown
Blue · #2B8FD9
Monitor pillar · Good verdict · CTAs use blue-500
Aqua · #1FC8C8
History pillar · charts · highlights
Green · #1AC863
Alerts pillar · Excellent verdict · pass state
Yellow · #FFB200
Suggestions pillar · Medium verdict · nudges
Red · #F43535
Map pillar · Bad verdict · critical alert
Sky · #3D95FF
Supporting accent · info states
Neutral · #0D1E2E
Body text · app chrome · icon strokes
Dark · #071525
Focus surfaces · onboarding · dark mode base
// Type Scale · Nunito · 5 Weights
Display Your water, measured. 40 / 700
Heading 01 Good morning, Ciarán 32 / 700
Heading 02 Last tested today · 08:42 24 / 600
Heading 03 Six sensor parameters 20 / 600
Body Calm, factual, never alarmist. A knowledgeable friend who happens to be a water engineer. 16 / 400
Label Last test · 08:42 13 / 500
// Spacing Scale · Base-4
4
8
12
16
20
24
32
40
48
64
// 03

Three Coordinated Surfaces

Fior lives in three live surfaces: the case study you’re reading, a mobile-first process doc, and an interactive hi-fi prototype. They share the same palette, the same Verdict Ladder, the same calm voice. One design system. Three voices.

CLASSIFIED // DOC-ID · RM-2026-H2O
// 004 · CASE
FIOR
Water Truth
fíor · Irish · true, real, pure
// CASE STUDY · CLASSIFIED
// 01 · Surface

Case Study

The narrative entry: the story you’re inside of right now.

Vanilla HTML Death Stranding aesthetic
// reading now · this surface
// 4 MODES Process → Wireframe → Prototype → System
// PROCESS DOC · MOBILE-FIRST
// 02 · Surface

Process Doc

A single mobile-first document with four modes: Process, Wireframe, Prototype, System. Walks the design trail from problem framing to the embryonic system.

Vanilla HTML Zero deps 4 Modes Mobile-first
Open Process Doc →
// HI-FI · PROTOTYPE
// 03 · Surface

Hi-Fi Prototype

Vite + React 18 + TypeScript strict. 15 screens across 6 phases and 3 branches. The 4-state Verdict Ladder lives here, end-to-end. Hash routing, no build SSR.

React + TS 15 Screens 29 Tests ~84 KB · WCAG AA
Launch Prototype →
// 04

When Figma Was the One Drifting

Behind the three surfaces sits the Figma file, where Fior started: 27 screens across seven feature flows. The hi-fi prototype came later, as a 15-screen navigable path. In October 2026 I audited the file against the shipped code and rebuilt it in three passes, from v3 to v3.2. Every number in this chapter is as of v3.2.

Act 01
01

“Figma Wins”, Except Figma Had Drifted

Parity in the Number · Drift in the Value

The process doc’s System page used to say it plainly: “Figma is the source of truth. If this artefact ever drifts, Figma wins.” The audit said the opposite. The Figma file was the one behind: still set in Inter, still on the old type scale. The variable count matched the online system to the unit. The values didn’t.

The findings that mattered were the ones no screenshot shows. The input border was aliased to a spacing token. The button radius token was bound to rectangles that weren’t buttons. And the Button component itself had been deleted while screens were still using it.

// Method · Safety Re-bind

Before changing any value, I re-bound each layer to a token with the same value, so nothing on screen could move while the structure underneath did. Only then did the values change. Across 27 screens, zero regressions.

The sentence came off the site. The System page now publishes the rule the work actually follows: the shipped code sets the values; Figma mirrors it, except where the code fails accessibility.

Act 02
02

Make the Sentence True

67 → 0 Loose Colours · Inside 27 Screens

The documentation also claimed zero hex hardcoded. Inside the screens, that wasn’t true yet. I could have softened the sentence. I fixed the file instead: the 67 loose colours inside the screens now resolve to variables, and gradients live as paint styles, because Figma’s plugin API can’t bind a variable to a gradient stop. The census reads zero across all 27 screens.

The structure follows the code’s API too. Button became a Type × State set, because in code disabled is a state, not a type. The nav bar’s active tab became a variant, mirroring activeTabForScreen(), and adding the Map tab fixed a navigation error on the way.

// Key Decision · Accessibility Beats Parity

Where parity and accessibility disagreed, accessibility won, in both directions. The component library’s success button failed AA at 2.2:1, so the code moved to green-600 (5.6:1). Figma’s input error text failed AA at 3.9:1, so Figma moved to the code’s red-600 (7.9:1).

The audit found the button everyone sees failing too: white on blue-400 was 3.48:1. Primary moved to blue-500 (5.22:1), in code first, then in Figma.

An automated check agrees: axe-core 4.10, WCAG 2.1 A and AA rules, run on the live hi-fi at 375 px and on desktop, reports 0 violations across the 15 screens (Oct 2026). It is an automated check, not a full audit: no screen reader was involved.

Act 03
03

Figma Learns the Code’s Words

262 of 294 Code-Named · 32 Say Why Not

Figma’s Token Reference page is the only bridge to code for anyone without a paid seat. It listed CSS names that don’t exist in the code, like --color-primary and --spacing-1.

I didn’t rename Figma to look like the code. Renaming would have produced the worst kind of drift: same name, different value. Instead, each variable learned the code’s word for it, but only where the resolved values match. 262 of 294 do. The other 32 carry a dash and a reason.

Code (hi-fi CSS) Value Figma variable (Fior/Semantic)
--text-primary#0D1E2Ecolor/text-primary
--text-muted#6A849Ecolor/text-secondary
–#8FA5BCcolor/text-tertiary
--text-secondary#354D62color/text-strong
--text-tertiary#4E6680color/text-meta
–#B8C8D8color/text-disabled
--text-on-deep#FFFFFFcolor/text-inverse
Transcribed from the Token Reference page of the Figma file, v3.2, text colours. The word secondary sits on two different rows: Figma’s color/text-secondary is the code’s --text-muted, while the code’s --text-secondary is Figma’s color/text-strong. A rename would have merged them. The two dashes are Figma-only text colours: the prototype has no semantic text token at those values.

There’s no Code Connect: it needs an Organization or Enterprise plan, and the file is on Pro. Instead, every component set names its source file and props in its description.

A review still caught me. A value match is necessary, not sufficient: one pair passed my script and was still wrong, because the shared button weight matched the primary button and hid two lighter variants in code. The rule I kept: role beats nearest value.

// Method · Five Independent Reviewers

Before any of this went public, five independent reviewers, separate AI agents with one lens each, read the material against the code, the Figma file and the site. They found errors I had written with confidence: an “AAA” that was AA, comparisons between counts with different scopes, and the site still saying “Figma wins”. All fixed before this chapter was written.

Every number here is a census I can rerun, and every bulk write ran all-or-nothing: the script compared every pair and wrote nothing if a single one diverged.

// 05

The brief wasn’t in the lab. It was in the bin.

Irish public water is 99.8% compliant, and Dublin recycling bins are full of bottled water. That gap is the real brief. People weren’t fleeing unsafe water; they were fleeing the absence of personal confirmation. Research lives less in what people say is wrong, and more in the small workaround they’ve quietly built around it.

Most health apps lead with the reading (TDS 128, pH 7.2) and leave you to translate it. That’s a failure of voice dressed up as transparency. Fior’s Verdict Ladder leads with the state and tucks the numbers one tap away. Calm isn’t a tone; it’s an information-architecture decision.

All of it built solo, 15 screens, 294 variables, 29 passing tests, WCAG 2.1 AA: I designed hands-on in Figma and directed the system (personas, IA, the calm voice, the verdict ladder as the central decision), while AI wrote the code under my direction.