Gray
Color
Brooklyn, New York

Hi, I'mHyewonand I'm aProduct Designer.

I design simple, intuitive products with AI-forward thinking and tools. With a background in cognitive psychology and documentary filmmaking, I care about the "why" behind how people think, feel, and decide.

Most recently, I designed with Experian.

Email directly
Copy address

Playground

Hold button

<!-- HTML -->
<button class="hold"><span>Hold</span></button>

/* CSS */
.hold {
  position: relative; width: 112px; height: 44px; border: 0; border-radius: 22px;
  background: linear-gradient(#D60000, #D60000) left / 0% 100% no-repeat #EEE8E6;
  box-shadow: 4px 4px 10px #D3C9C5, -4px -4px 10px #FFF;
  font: 500 14px Inter, sans-serif; color: #3A3433;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: manipulation;
  transition: background-size .3s;
}
.hold:active {
  background-size: 100% 100%; transition: background-size 1.1s linear;
  box-shadow: inset 3px 3px 8px #D3C9C5, inset -3px -3px 8px #FFF;
}
.hold.done { background: #1F45D3; }
.hold.done { animation: pop .45s cubic-bezier(.34, 1.56, .64, 1); }
@keyframes pop { 50% { transform: scale(1.08); } }
.hold span { position: relative; transition: color .2s; }
.hold:active span { color: #FFF; transition: color .2s .4s; }
.hold.done span { color: #FFF; }
.bit {
  position: absolute; left: 50%; top: 50%; width: 5px; height: 3px;
  border-radius: 1px; pointer-events: none;
}

// JS  ($ = querySelector)
const b = $('.hold'), t = $('span');
const burst = () => ['#D60000', '#1F45D3', '#CFC6C2'].flatMap(c => [c, c, c, c]).forEach(c => {
  const i = document.createElement('i'), a = Math.random() * 6.28, d = 28 + Math.random() * 26;
  i.className = 'bit'; i.style.background = c; b.append(i);
  i.animate([{ transform: 'none', opacity: 1 },
    { transform: `translate(${Math.cos(a) * d * 1.5}px, ${Math.sin(a) * d + 14}px) rotate(${a * 90}deg)`, opacity: 0 }],
    { duration: 900, easing: 'cubic-bezier(.2, .7, .4, 1)' }).onfinish = () => i.remove();
});
b.ontouchstart = () => {};
b.ontransitionend = e => {
  if (e.propertyName !== 'background-size' || !b.matches(':active') || b.classList.contains('done')) return;
  b.classList.add('done'); t.textContent = 'Sent'; burst();
  setTimeout(() => { b.classList.remove('done'); t.textContent = 'Hold'; }, 1600);
};

Day and night

<!-- HTML -->
<label class="sky"><input type="checkbox"><i></i></label>

/* CSS */
.sky {
  display: block; width: 84px; height: 44px;
  border-radius: 22px; background: #EEE8E6;
  box-shadow: inset 3px 3px 8px #D3C9C5, inset -3px -3px 8px #FFF;
  transition: background .5s;
}
.sky input { display: none; }
.sky i {
  display: block; width: 32px; height: 32px; margin: 6px;
  border-radius: 50%; background: #D60000;
  box-shadow: 0 0 0 5px #D6000022;
  transition: .5s cubic-bezier(.34, 1.56, .64, 1);
}
.sky:has(:checked) { background: #1F45D3; }
.sky :checked + i {
  transform: translateX(40px); background: #F1F2FF;
  box-shadow: inset -9px -4px 0 #C7CCF2;
}

Tabs

<!-- HTML -->
<nav class="tabs">
  <input type="radio" name="t" id="a" checked><label for="a">Work</label>
  <input type="radio" name="t" id="b"><label for="b">Play</label>
  <span></span>
</nav>

/* CSS */
.tabs {
  position: relative; display: grid;
  grid-template-columns: repeat(2, 52px); padding: 4px;
  border-radius: 14px; background: #EEE8E6;
  box-shadow: inset 3px 3px 8px #D3C9C5, inset -3px -3px 8px #FFF;
  font: 500 12px Inter, sans-serif;
}
.tabs input { display: none; }
.tabs label {
  position: relative; z-index: 1; padding: 9px 0;
  text-align: center; color: #8A807D; transition: color .3s;
}
.tabs :checked + label { color: #1F45D3; }
#b:checked + label { color: #D60000; }
.tabs span {
  position: absolute; inset: 4px auto 4px 4px; width: 52px;
  border-radius: 10px; background: #EEE8E6;
  box-shadow: 3px 3px 8px #D3C9C5, -3px -3px 8px #FFF;
  transition: transform .45s cubic-bezier(.34, 1.56, .64, 1);
}
#b:checked ~ span { transform: translateX(52px); }

Slider

<!-- HTML -->
<label class="vol" style="--n: .6">
  <output>60</output><input type="range" value="60">
</label>

/* CSS */
.vol {
  position: relative; display: block; width: 120px;
  --c: color-mix(in srgb, #D60000 calc(var(--n) * var(--n) * 100%), #1F45D3);
}
.vol input {
  width: 100%; height: 10px; margin: 0;
  -webkit-appearance: none; appearance: none; border-radius: 5px;
  background: linear-gradient(90deg, var(--c) calc(12px + (100% - 24px) * var(--n)), #EEE8E6 0);
  box-shadow: inset 2px 2px 5px #D3C9C5, inset -2px -2px 5px #FFF;
}
.vol input::-webkit-slider-thumb {
  -webkit-appearance: none; width: 24px; height: 24px;
  border-radius: 50%; background: #EEE8E6;
  box-shadow: 3px 3px 7px #C9BEBA, -3px -3px 7px #FFF;
}
.vol input::-moz-range-thumb {
  width: 24px; height: 24px; border: 0;
  border-radius: 50%; background: #EEE8E6;
  box-shadow: 3px 3px 7px #C9BEBA, -3px -3px 7px #FFF;
}
.vol output {
  position: absolute; bottom: 20px;
  left: calc(12px + (100% - 24px) * var(--n));
  transform: translateX(-50%); transition: transform .3s;
  font: 600 12px Inter, sans-serif; color: var(--c);
}
.vol:active output { transform: translateX(-50%) translateY(-4px) scale(1.2); }

// JS  ($ = querySelector)
const v = $('.vol'), i = $('input'), o = $('output');
i.oninput = () => { v.style.setProperty('--n', i.value / 100); o.value = i.value; };

Checkbox

<!-- HTML -->
<label class="todo">
  <input type="checkbox">
  <svg viewBox="0 0 24 24"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>
  <span>Water the<br>plants</span>
</label>

/* CSS */
.todo {
  display: flex; align-items: center; gap: 10px;
  font: 500 13px/1.3 Inter, sans-serif; color: #3A3433;
  transition: color .3s;
}
.todo input { display: none; }
.todo svg {
  width: 26px; height: 26px; flex: none;
  border-radius: 8px; background: #EEE8E6;
  box-shadow: 3px 3px 7px #D3C9C5, -3px -3px 7px #FFF;
  fill: none; stroke: #D60000; stroke-width: 2.6;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 21; stroke-dashoffset: 21;
  transition: stroke-dashoffset .4s, box-shadow .3s;
}
.todo:has(:checked) { color: #A39996; text-decoration: line-through; }
.todo :checked + svg {
  stroke-dashoffset: 0;
  box-shadow: inset 2px 2px 5px #D3C9C5, inset -2px -2px 5px #FFF;
}
0 of 15 tidied
Setting up the room…

My room, before coffee

Ahh. Much better.
Taking something messy and making it feel calm is pretty much my whole job.

커피

Drag to wipe the glass