Blink Bean
A tiny bean-shaped CSS character that blinks with one gentle keyframe animation.
← a soft little friend who blinks at you.
what you'll build
Here's the plan
A tiny original CoDoodle character — a soft bean with a sprout, a smiling face, and a gentle blink, built from pure divs and CSS (no SVG, no images).
You’ll learn
- Shaping a bean with an eight-value border-radius
- Stacking a face out of simple absolute-positioned shapes
- Holding a CSS blink open most of the loop for a natural flicker
- Skill level
- Beginner
- Time
- ~15 min
- Tools
- Just a browser
- Code type
- HTML + CSS
- Lines
- ~42
the steps
Build it, one change at a time
Shape the bean
A plain box bulges into a bean when you round the top more than the bottom — the eight-value border-radius curves the silhouette. A soft oval underneath becomes the ground shadow.
.bean-wrap { position: relative; width: 200px; height: 180px; } .shadow { position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%); width: 110px; height: 15px; background: #2B3A55; border-radius: 50%; opacity: 0.12; } .bean { position: absolute; left: 50%; top: 18px; transform: translateX(-50%); width: 140px; height: 135px; background: #FFFDF7; border: 6px solid #2B3A55; border-radius: 50% 50% 46% 46% / 58% 58% 42% 42%; }it's a bean, sort of.
Give it a face
Two ink dots for eyes, coral blush, and a bottom-only border arc for a smile. Each piece is absolutely placed inside the bean.
.eye { position: absolute; top: 46px; width: 14px; height: 18px; background: #2B3A55; border-radius: 50%; } .eye-left { left: 40px; } .eye-right { right: 40px; } .blush { position: absolute; top: 70px; width: 16px; height: 10px; background: #FFB3AD; border-radius: 50%; opacity: 0.85; } .smile { position: absolute; top: 74px; left: 50%; transform: translateX(-50%); width: 28px; height: 14px; border: 3px solid #2B3A55; border-top: none; border-radius: 0 0 28px 28px; }now it's looking at you.
Add arms, a sprout & a tiny scene
Little side arms, a mint sprout on top, a grass tuft, and a sparkle made from two pseudo-elements crossing into a plus.
.arm { position: absolute; top: 74px; width: 14px; height: 22px; background: #FFFDF7; border: 5px solid #2B3A55; border-radius: 50%; } .sprout { position: absolute; top: -16px; left: 50%; transform: translateX(-50%); width: 8px; height: 16px; background: #5ECBB4; border: 4px solid #2B3A55; border-radius: 50% 50% 6px 6px; } .sparkle::before, .sparkle::after { content: ""; position: absolute; background: #FFD84D; border-radius: 2px; }a whole tiny scene.
Make it blink & breathe
A blink keyframe holds the eyes open almost the whole loop, then squashes them for a flicker. A subtle breathe bobs the whole bean — keeping translateX(-50%) inside the keyframe so it stays centered while it bobs.
.eye { transform-origin: center; animation: bean-blink 4s ease-in-out infinite; } .bean { animation: bean-breathe 3.4s ease-in-out infinite; } @keyframes bean-blink { 0%, 90%, 100% { transform: scaleY(1); } 95% { transform: scaleY(0.08); } } @keyframes bean-breathe { 0%, 100% { transform: translateX(-50%) translateY(0) rotate(0deg); } 50% { transform: translateX(-50%) translateY(-3px) rotate(-1.5deg); } }it's alive — and it blinks at you.
the complete code
Everything, in one place
Skipped straight to the end? Welcome. Copy the whole thing, download it, or open it in the Playground to start remixing.
<div class="bean-wrap">
<div class="shadow"></div>
<div class="bean">
<div class="eye eye-left"></div>
<div class="eye eye-right"></div>
<div class="blush blush-left"></div>
<div class="blush blush-right"></div>
<div class="smile"></div>
<div class="arm arm-left"></div>
<div class="arm arm-right"></div>
<div class="sprout"></div>
</div>
<div class="grass"></div>
<div class="sparkle"></div>
</div>
tips & gotchas
Mistakes we actually made
If the bean looks like an egg, your border-radius top values are too even — round them more.
Hold the blink open for ~90% of the loop; a 50/50 blink reads as a twitch, not a blink.
Keep translateX(-50%) inside the breathe keyframe or the bean drifts off-center while it bobs.
make it yours
Remix it
- Sleepy beaneasy
Slow the blink to 6s and add a tiny 'z' sparkle.
- Bean gangmedium
Reuse the bean at three sizes and stagger the blinks so they never sync.
challenge extension
Give the bean a second expression — a surprised 'o' mouth that pops in for one frame right after each blink.