Field Kitchen

Client

Independent Study

Category

Interaction + Interface Study

Year

2026

Field Kitchen ingredient interface
Field Kitchen ingredient interface

Field Kitchen is a self-directed interface study exploring food as an information system. Using a small Pacific Northwest ingredient set, I designed the product structure, interaction model, visual system, and working prototype around ways of organizing ingredients by season, place, preparation, nutrition, and pairing rather than primarily through recipes. The prototype includes an Ingredient Library, Dataset Index, Pairing Field, Seasonal Map, Nutrient View, and ingredient detail states, using spatial relationships, filtering, and comparative views to make different kinds of food data useful without turning the experience into a conventional database or dashboard.

React · TypeScript · Motion · Vite · Tailwind · Data Visualization

Field Kitchen is a working interactive prototype rather than a fixed set of screens. Its views translate different kinds of food relationships into different interface forms: browsing in the Ingredient Library, preparation relationships in the Pairing Field, harvest timing in the Seasonal Map, and spatial comparison in Nutrient View. The prototype also explores how a quiet, editorial interface can support relatively dense information without feeling like a conventional dashboard.


Pairing Field Interaction

function getPairingStrength(
  selected: Ingredient,
  other: Ingredient
): PairingStrength {
  if (selected.id === other.id) return null

  const selectedListsOther = selected.pairings.some(
    p => nameMatch(other.name, p)
  )

  const otherListsSelected = other.pairings.some(
    p => nameMatch(selected.name, p)
  )

  const sharedPreps = selected.preparations.filter(
    p => other.preparations.includes(p)
  ).length

  if (selectedListsOther && otherListsSelected) {
    return sharedPreps >= 2 ? "strong" : "medium"
  }

  if (selectedListsOther || otherListsSelected) {
    return "medium"
  }

  if (sharedPreps >= 2) {
    return "experimental"
  }

  return null
}
function getPairingStrength(
  selected: Ingredient,
  other: Ingredient
): PairingStrength {
  if (selected.id === other.id) return null

  const selectedListsOther = selected.pairings.some(
    p => nameMatch(other.name, p)
  )

  const otherListsSelected = other.pairings.some(
    p => nameMatch(selected.name, p)
  )

  const sharedPreps = selected.preparations.filter(
    p => other.preparations.includes(p)
  ).length

  if (selectedListsOther && otherListsSelected) {
    return sharedPreps >= 2 ? "strong" : "medium"
  }

  if (selectedListsOther || otherListsSelected) {
    return "medium"
  }

  if (sharedPreps >= 2) {
    return "experimental"
  }

  return null
}
function getPairingStrength(
  selected: Ingredient,
  other: Ingredient
): PairingStrength {
  if (selected.id === other.id) return null

  const selectedListsOther = selected.pairings.some(
    p => nameMatch(other.name, p)
  )

  const otherListsSelected = other.pairings.some(
    p => nameMatch(selected.name, p)
  )

  const sharedPreps = selected.preparations.filter(
    p => other.preparations.includes(p)
  ).length

  if (selectedListsOther && otherListsSelected) {
    return sharedPreps >= 2 ? "strong" : "medium"
  }

  if (selectedListsOther || otherListsSelected) {
    return "medium"
  }

  if (sharedPreps >= 2) {
    return "experimental"
  }

  return null
}

A shared ingredient dataset drives the connected views, so changing filters or metrics reshapes the same underlying information across seasonal, nutritional, sourcing, preparation, and pairing relationships. Motion is used primarily to make those transitions readable, giving the interface a slower, more exploratory quality.