Subscribe

Creating Kirby with CSS art

✍️

How to create a Kirby in CSS

4 Sep, 2021 Β· 10 min read

I felt like doing another CSS Art again, so today, we'll be recreating Kirby using nothing but CSS ✨.

The result will be the amazing Kirby you see below.

Kirby with CSS art

Analysing Kirby

To start with our CSS art, we'll first evaluate what kind of shapes we need.

I've created this quick image to show the shapes we'll be using:

The shapes of Kirby

As you can see, almost all shapes are some round shapes, which we'll overlap and sometimes hide under another layer.

Let's start by creating the HTML elements we'll need:

<div class="kirby">
  <div class="arm arm-left"></div>
  <div class="arm arm-right"></div>
  <div class="foot foot-left"></div>
  <div class="foot foot-right"></div>
  <div class="mouth"></div>
  <div class="eye eye-left">
    <div class="inner"></div>
  </div>
  <div class="eye eye-right">
    <div class="inner"></div>
  </div>
</div>

As you can see, the Kirby has its primary shape: the body, arms and feet, and the mouth and eyes.

Now we can move on and give this some styling!

Styling Kirby with CSS

Before starting with the styling, let's define the colors we need to color in our Kirby.

:root {
  --pink: #ff9ec8;
  --deep-pink: #f20079;
  --shadow: #cb91b2;
  --cheek: #e5649c;
}

Then we can move to the body part of our Kirby. It's a big round blob.

.kirby {
  width: 500px;
  height: 432px;
  background: var(--pink);
  position: absolute;
  border-radius: 50%;
  border: 1rem solid black;
}

Then with the pseudo :before selector, we give Kirby its shadow shape.

.kirby {
  &:before {
    content: '';
    width: 118%;
    height: 13.8%;
    background: var(--shadow);
    position: absolute;
    bottom: -10%;
    border-radius: 50%;
    z-index: -2;
    left: -7%;
  }
}

These pseudo-elements are great for creating shapes like the shadow, which are small details in the picture.

Then let's move on to the arms, we created two divs for them, but they have their custom style.

.arm {
  &-left {
    width: 31%;
    height: 32%;
    background: var(--pink);
    position: relative;
    top: 44%;
    border-radius: 50% 40%;
    transform: rotate(342deg);
    border: 1rem solid black;
    left: -13.5%;
    &:before {
      content: '';
      width: 70%;
      height: 150%;
      border-radius: 50%;
      background: var(--pink);
      position: absolute;
      left: 47%;
      top: -25%;
    }
  }
}

This will create a basic round shape, and we'll use the before element to offset it to the background.

To show you how that works, let's see the element without the overlapping before first:

Kirby entire arm

And for the example, I've made the overlap a different color so you can see what happens.

Overlapping shadow

The trick with the arm is to make the overlapping element the same color as the body, so it disappears into the background.

Then we have the right arm, which is a bit easier as it goes behind the body. In our case, we'll use z-index to offset it.

.arm {
  &-right {
    width: 31%;
    height: 32%;
    background: var(--pink);
    position: relative;
    top: 2%;
    border-radius: 50% 40%;
    transform: rotate(215deg);
    border: 1rem solid black;
    right: -75%;
    z-index: -1;
  }
}

The feet are similar, but we'll use a double border-radius to offset the shape more.

.foot {
  &-left {
    position: absolute;
    width: 40%;
    height: 18%;
    background: var(--deep-pink);
    border-radius: 44% 56% 63% 37% / 78% 59% 41% 22%;
    left: 1%;
    bottom: -6.5%;
    border: 1rem solid black;
    z-index: -1;
  }
  &-right {
    position: absolute;
    width: 40%;
    height: 18%;
    background: var(--deep-pink);
    border-radius: 42% 58% 35% 65%/54% 82% 18% 46%;
    right: -2%;
    bottom: -4%;
    border: 1rem solid black;
    z-index: -1;
  }
}

The foot shape in total will look like this. However, it's placed more to the back by the use of z-index again.

Kirby foot example

The next thing we have is the mouth, which is tricky to get. We'll create a square with rounded corners, but only color the top color.

.mouth {
  position: absolute;
  top: 52%;
  left: 63%;
  width: 2.5em;
  height: 1.5em;
  transform: translateX(-50%);
  color: #000;
  border: 0.625rem solid transparent;
  border-top-color: currentColor;
  border-radius: 41%;
  transform: rotate(352deg);
}

I've made the rest of the border red in the image below to see what's happening.

Kirby's mouth

Then the mouth part also has a before and after for the cheek shading. This was easier because it's kind of offset from the mouth.

.mouth {
  &:before {
    content: '';
    position: absolute;
    width: 200%;
    height: 150%;
    background: var(--cheek);
    border-radius: 50%;
    left: -400%;
    transform: rotate(5deg);
    top: -190%;
  }
  &:after {
    content: '';
    position: absolute;
    width: 143%;
    height: 120%;
    background: var(--cheek);
    border-radius: 50%;
    right: -300%;
    transform: rotate(353deg);
    top: -170%;
  }
}

Then for the eyes, we use another two rounded elements. We use the before and after to create the eyebrow and hide whatever is left under the eyebrow.

.eye {
  position: absolute;
  background: black;
  &-left {
    width: 10%;
    height: 28%;
    border-radius: 50%;
    top: 21%;
    left: 49.5%;
    transform: rotate(351deg);
    &:after {
      transform: rotate(33deg);
      content: '';
      width: 190%;
      height: 1rem;
      background: var(--pink);
      position: absolute;
      top: -4%;
      left: -38%;
      z-index: 1;
    }
    &:before {
      content: '';
      width: 168%;
      height: 0.75rem;
      background: black;
      position: absolute;
      border-radius: 30%;
      z-index: 9;
      transform: rotate(33deg);
      top: 0.625rem;
      left: -28%;
    }
  }
  &-right {
    width: 9%;
    height: 27%;
    border-radius: 50%;
    top: 19%;
    left: 67.5%;
    transform: rotate(348deg);
    &:after {
      transform: rotate(327deg);
      content: '';
      width: 190%;
      height: 1rem;
      background: var(--pink);
      position: absolute;
      top: -4%;
      left: -37%;
    }
    &:before {
      content: '';
      width: 169%;
      height: 0.75rem;
      background: black;
      position: absolute;
      border-radius: 30%;
      z-index: 9;
      transform: rotate(327deg);
      top: 6%;
      left: -26%;
    }
  }
}

Then, you might have spotted three things going on in the inner part.

  • The white pupil
  • The black pupil in the middle
  • The blue part at the bottom

To recreate this, we'll place the black part first and use the before and after selectors for the white and blue parts.

.eye {
  .inner {
    width: 60%;
    height: 56%;
    background: #000;
    border-radius: 50%;
    position: absolute;
    top: 25%;
    left: 19%;
    &:before {
      content: '';
      position: absolute;
      width: 105%;
      height: 100%;
      background: #0164c7;
      border-radius: 50%;
      bottom: -20%;
      z-index: -1;
    }
    &:after {
      content: '';
      position: absolute;
      width: 105%;
      height: 102%;
      background: white;
      border-radius: 50%;
      top: -34%;
      z-index: 0;
    }
  }
}

And that's it. We now have our excellent CSS-powered Kirby.

I hope you enjoyed this article and found some valuable tips for your artwork.

You can also view the complete code example on the following CodePen.

See the Pen by Chris Bongers (@rebelchris) on CodePen.

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Spread the knowledge with fellow developers on Twitter
Tweet this tip
Powered by Webmentions - Learn more

Read next πŸ“–

Bringing perspective to CSS

7 Aug, 2022 Β· 2 min read

Bringing perspective to CSS

Creating a 3D Cylinder shape in CSS

29 Jul, 2022 Β· 3 min read

Creating a 3D Cylinder shape in CSS

Join <html><head><script data-ezscrex='false' data-cfasync='false' data-pagespeed-no-defer>var __ez=__ez||{};__ez.stms=Date.now();__ez.evt={};__ez.script={};__ez.ck=__ez.ck||{};__ez.template={};__ez.template.isOrig=true;__ez.queue=function(){var e=0,i=0,t=[],n=!1,s=[],r=[],o=!0,a=function(e,i,n,s,r,o,a){var l=this;this.name=e,this.funcName=i,this.parameters=null===n?null:n instanceof Array?n:[n],this.isBlock=s,this.blockedBy=r,this.deleteWhenComplete=o,this.isError=!1,this.isComplete=!1,this.isInitialized=!1,this.proceedIfError=a,this.isTimeDelay=!1,this.process=function(){u("... func = "+e),l.isInitialized=!0,l.isComplete=!0,u("... func.apply: "+e);var i=l.funcName.split("."),n=null;i.length>3||(n=3===i.length?window[i[0]][i[1]][i[2]]:2===i.length?window[i[0]][i[1]]:window[l.funcName]),null!=n&&n.apply(null,this.parameters),!0===l.deleteWhenComplete&&delete t[e],!0===l.isBlock&&(u("----- F'D: "+l.name),f())}},l=function(e,i,t,n,s,r,o){var a=this;this.name=e,this.path=i,this.async=s,this.defer=r,this.isBlock=t,this.blockedBy=n,this.isInitialized=!1,this.isError=!1,this.isComplete=!1,this.proceedIfError=o,this.isTimeDelay=!1,this.isPath=function(e){return"/"===e[0]&&"/"!==e[1]},this.getSrc=function(e){return void 0!==window.__ezScriptHost&&this.isPath(e)?window.__ezScriptHost+e:e},this.process=function(){a.isInitialized=!0,u("... file = "+e);var i=document.createElement("script");i.src=this.getSrc(this.path),!0===s?i.async=!0:!0===r&&(i.defer=!0),i.onerror=function(){u("----- ERR'D: "+a.name),a.isError=!0,!0===a.isBlock&&f()},i.onreadystatechange=i.onload=function(){var e=i.readyState;u("----- F'D: "+a.name),e&&!/loaded|complete/.test(e)||(a.isComplete=!0,!0===a.isBlock&&f())},document.getElementsByTagName("head")[0].appendChild(i)}},c=function(e,i){this.name=e,this.path="",this.async=!1,this.defer=!1,this.isBlock=!1,this.blockedBy=[],this.isInitialized=!0,this.isError=!1,this.isComplete=i,this.proceedIfError=!1,this.isTimeDelay=!1,this.process=function(){}};function d(e){!0!==h(e)&&0!=o&&e.process()}function h(e){if(!0===e.isTimeDelay&&!1===n)return u(e.name+" blocked = TIME DELAY!"),!0;if(e.blockedBy instanceof Array)for(var i=0;i<e.blockedBy.length;i++){var s=e.blockedBy[i];if(!1===t.hasOwnProperty(s))return u(e.name+" blocked = "+s),!0;if(!0===e.proceedIfError&&!0===t[s].isError)return!1;if(!1===t[s].isComplete)return u(e.name+" blocked = "+s),!0}return!1}function u(e){var i=window.location.href,t=new RegExp("[?&]ezq=([^&#]*)","i").exec(i);"1"===(t?t[1]:null)&&console.debug(e)}function f(){++e>200||(u("let's go"),m(s),m(r))}function m(e){for(var i in e)if(!1!==e.hasOwnProperty(i)){var t=e[i];!0===t.isComplete||h(t)||!0===t.isInitialized||!0===t.isError?!0===t.isError?u(t.name+": error"):!0===t.isComplete?u(t.name+": complete already"):!0===t.isInitialized&&u(t.name+": initialized already"):t.process()}}return window.addEventListener("load",(function(){setTimeout((function(){n=!0,u("TDELAY -----"),f()}),5e3)}),!1),{addFile:function(e,i,n,o,a,c,h,u){var f=new l(e,i,n,o,a,c,h);!0===u?s[e]=f:r[e]=f,t[e]=f,d(f)},addDelayFile:function(e,i){var n=new l(e,i,!1,[],!1,!1,!0);n.isTimeDelay=!0,u(e+" ... FILE! TDELAY"),r[e]=n,t[e]=n,d(n)},addFunc:function(e,n,o,l,c,h,u,f,m){!0===h&&(e=e+"_"+i++);var p=new a(e,n,o,l,c,u,f);!0===m?s[e]=p:r[e]=p,t[e]=p,d(p)},addDelayFunc:function(e,i,n){var s=new a(e,i,n,!1,[],!0,!0);s.isTimeDelay=!0,u(e+" ... FUNCTION! TDELAY"),r[e]=s,t[e]=s,d(s)},items:t,processAll:f,setallowLoad:function(e){o=e},markLoaded:function(e){if(e&&0!==e.length){if(e in t){var i=t[e];!0===i.isComplete?u(i.name+" "+e+": error loaded duplicate"):(i.isComplete=!0,i.isInitialized=!0)}else t[e]=new c(e,!0);u("markLoaded dummyfile: "+t[e].name)}},logWhatsBlocked:function(){for(var e in t)!1!==t.hasOwnProperty(e)&&h(t[e])}}}();__ez.evt.add=function(e,t,n){e.addEventListener?e.addEventListener(t,n,!1):e.attachEvent?e.attachEvent("on"+t,n):e["on"+t]=n()},__ez.evt.remove=function(e,t,n){e.removeEventListener?e.removeEventListener(t,n,!1):e.detachEvent?e.detachEvent("on"+t,n):delete e["on"+t]};__ez.script.add=function(e){var t=document.createElement("script");t.src=e,t.async=!0,t.type="text/javascript",document.getElementsByTagName("head")[0].appendChild(t)};__ez.dot={};__ez.queue.addFile('/detroitchicago/boise.js', '/detroitchicago/boise.js?gcb=195-0&cb=2', true, [], true, false, true, false);__ez.queue.addFile('/detroitchicago/memphis.js', '/detroitchicago/memphis.js?gcb=195-0&cb=21', true, [], true, false, true, false);__ez.queue.addFile('/detroitchicago/minneapolis.js', '/detroitchicago/minneapolis.js?gcb=195-0&cb=4', true, [], true, false, true, false);__ez.queue.addFile('/detroitchicago/rochester.js', '/detroitchicago/rochester.js?gcb=195-0&cb=13', false, ['/detroitchicago/memphis.js','/detroitchicago/minneapolis.js'], true, false, true, false);!function(){var e;__ez.vep=(e=[],{Add:function(i,t){__ez.dot.isDefined(i)&&__ez.dot.isValid(t)&&e.push({type:"video",video_impression_id:i,domain_id:__ez.dot.getDID(),t_epoch:__ez.dot.getEpoch(0),data:__ez.dot.dataToStr(t)})},Fire:function(){if(void 0===document.visibilityState||"prerender"!==document.visibilityState){if(__ez.dot.isDefined(e)&&e.length>0)for(;e.length>0;){var i=5;i>e.length&&(i=e.length);var t=e.splice(0,i),o=__ez.dot.getURL("/detroitchicago/grapefruit.gif")+"?orig="+(!0===__ez.template.isOrig?1:0)+"&v="+btoa(JSON.stringify(t));__ez.dot.Fire(o)}e=[]}}})}();</script><script data-ezscrex='false' data-cfasync='false' data-pagespeed-no-defer>!function(){function e(i){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(i)}__ez.pel=function(){var i=[];function t(t,o,d,_,n,r,a,s){if(__ez.dot.isDefined(t)&&0!=__ez.dot.isAnyDefined(t.getSlotElementId,t.ElementId)){void 0===s&&(s=!1);var p=parseInt(__ez.dot.getTargeting(t,"ap")),f=__ez.dot.getSlotIID(t),u=__ez.dot.getAdUnit(t,s),z=parseInt(__ez.dot.getTargeting(t,"compid")),g=0,c=0,l=function(i){if("undefined"==typeof _ezim_d)return!1;var t=__ez.dot.getAdUnitPath(i).split("/").pop();if("object"===("undefined"==typeof _ezim_d?"undefined":e(_ezim_d))&&_ezim_d.hasOwnProperty(t))return _ezim_d[t];for(var o in _ezim_d)if(o.split("/").pop()===t)return _ezim_d[o];return!1}(t);"object"==e(l)&&(void 0!==l.creative_id&&(c=l.creative_id),void 0!==l.line_item_id&&(g=l.line_item_id)),__ez.dot.isDefined(f,u)&&__ez.dot.isValid(o)&&("0"===f&&!0!==s||""===u||i.push({type:"impression",impression_id:f,domain_id:__ez.dot.getDID(),unit:u,t_epoch:__ez.dot.getEpoch(0),revenue:d,est_revenue:_,ad_position:p,ad_size:"",bid_floor_filled:n,bid_floor_prev:r,stat_source_id:a,country_code:__ez.dot.getCC(),pageview_id:__ez.dot.getPageviewId(),comp_id:z,line_item_id:g,creative_id:c,data:__ez.dot.dataToStr(o),is_orig:s||__ez.template.isOrig}))}}function o(){void 0!==document.visibilityState&&"prerender"===document.visibilityState||(__ez.dot.isDefined(i)&&i.length>0&&[i.filter((function(e){return e.is_orig})),i.filter((function(e){return!e.is_orig}))].forEach((function(e){for(;e.length>0;){var i=e[0].is_orig||!1,t=5;t>e.length&&(t=e.length);var o=e.splice(0,t),d=__ez.dot.getURL("/porpoiseant/army.gif")+"?orig="+(!0===i?1:0)+"&sts="+btoa(JSON.stringify(o));(void 0!==window.isAmp&&isAmp||void 0!==window.ezWp&&ezWp)&&void 0!==window._ezaq&&_ezaq.hasOwnProperty("domain_id")&&(d+="&visit_uuid="+_ezaq.visit_uuid),__ez.dot.Fire(d)}})),i=[])}return{Add:t,AddAndFire:function(e,i){t(e,i,0,0,0,0,0),o()},AddAndFireOrig:function(e,i){t(e,i,0,0,0,0,0,!0),o()},AddById:function(e,t,o,d){var _=e.split("/");if(__ez.dot.isDefined(e)&&3===_.length&&__ez.dot.isValid(t)){var n=_[0],r={type:"impression",impression_id:_[2],domain_id:__ez.dot.getDID(),unit:n,t_epoch:__ez.dot.getEpoch(0),pageview_id:__ez.dot.getPageviewId(),data:__ez.dot.dataToStr(t),is_orig:o||__ez.template.isOrig};void 0!==d&&(r.revenue=d),i.push(r)}},Fire:o,GetPixels:function(){return i}}}()}();__ez.queue.addFile('/detroitchicago/raleigh.js', '/detroitchicago/raleigh.js?gcb=195-0&cb=6', false, [], true, false, true, false);__ez.queue.addFile('/detroitchicago/tampa.js', '/detroitchicago/tampa.js?gcb=195-0&cb=5', false, [], true, false, true, false);</script> <script data-ezscrex="false" data-cfasync="false">(function(){if("function"===typeof window.CustomEvent)return!1;window.CustomEvent=function(c,a){a=a||{bubbles:!1,cancelable:!1,detail:null};var b=document.createEvent("CustomEvent");b.initCustomEvent(c,a.bubbles,a.cancelable,a.detail);return b}})();</script><script data-ezscrex="false" data-cfasync="false">__ez.queue.addFile('/detroitchicago/tulsa.js', '/detroitchicago/tulsa.js?gcb=195-0&cb=7', false, [], true, false, true, false);</script><script type="text/javascript">var ezouid = "1";</script><base href="https://sendy.daily-dev-tips.com/api/subscribers/active-subscriber-count.php"><script type='text/javascript'> var ezoTemplate = 'old_site_noads'; if(typeof ezouid == 'undefined') { var ezouid = 'none'; } var ezoFormfactor = '1'; var ezo_elements_to_check = Array(); </script> <script data-ezscrex="false" type='text/javascript'> var soc_app_id = '0'; var did = 408517; var ezdomain = 'daily-dev-tips.com'; var ezoicSearchable = 1; </script> <script data-ezscrex="false" type="text/javascript" data-cfasync="false">var _ezaq = {"ad_cache_level":0,"ad_lazyload_version":0,"ad_load_version":0,"city":"The Dalles","country":"US","days_since_last_visit":-1,"domain_id":408517,"engaged_time_visit":0,"ezcache_level":1,"ezcache_skip_code":8,"form_factor_id":1,"framework_id":1,"is_return_visitor":false,"is_sitespeed":0,"last_page_load":"","last_pageview_id":"","lt_cache_level":0,"metro_code":820,"page_ad_positions":"","page_view_count":0,"page_view_id":"2daf0911-6958-4839-7e19-1b83275532ea","position_selection_id":0,"postal_code":"97058","pv_event_count":0,"response_size_orig":4,"response_time_orig":288,"serverid":"34.219.211.72:15785","state":"OR","t_epoch":1669521655,"template_id":120,"time_on_site_visit":0,"url":"https://sendy.daily-dev-tips.com/api/subscribers/active-subscriber-count.php","user_id":0,"word_count":1,"worst_bad_word_level":0};var _ezExtraQueries = "&ez_orig=1";</script> <script data-ezscrex='false' data-pagespeed-no-defer data-cfasync='false'> function create_ezolpl(pvID, rv) { var d = new Date(); d.setTime(d.getTime() + (365*24*60*60*1000)); var expires = "expires="+d.toUTCString(); __ez.ck.setByCat("ezux_lpl_408517=" + new Date().getTime() + "|" + pvID + "|" + rv + "; " + expires, 3); } function attach_ezolpl(pvID, rv) { if (document.readyState === "complete") { create_ezolpl(pvID, rv); } if(window.attachEvent) { window.attachEvent("onload", create_ezolpl, pvID, rv); } else { if(window.onload) { var curronload = window.onload; var newonload = function(evt) { curronload(evt); create_ezolpl(pvID, rv); }; window.onload = newonload; } else { window.onload = create_ezolpl.bind(null, pvID, rv); } } } __ez.queue.addFunc("attach_ezolpl", "attach_ezolpl", ["2daf0911-6958-4839-7e19-1b83275532ea", "false"], false, ['/detroitchicago/boise.js'], true, false, false, false); </script></head><body>1894<script type='text/javascript' style='display:none;' async> __ez.queue.addFile('/detroitchicago/edmonton.webp', '/detroitchicago/edmonton.webp?a=a&cb=0&shcb=34', true, ['/detroitchicago/minneapolis.js'], true, false, false, false); __ez.queue.addFile('/porpoiseant/jellyfish.webp', '/porpoiseant/jellyfish.webp?a=a&cb=0&shcb=34', false, [], true, false, false, false); </script> <script>__ez.queue.addFile('/tardisrocinante/vitals.js', '/tardisrocinante/vitals.js?gcb=0&cb=3', false, ['/detroitchicago/minneapolis.js'], true, false, true, false);</script> <script type="text/javascript" data-cfasync="false"></script> <script>var _audins_dom="daily_dev_tips_com",_audins_did=408517;__ez.queue.addDelayFunc("audins.js","__ez.script.add", "//go.ezoic.net/detroitchicago/audins.js?cb=195-0");</script><noscript><div style="display:none;"><img src="//pixel.quantserve.com/pixel/p-31iz6hfFutd16.gif?labels=Domain.daily_dev_tips_com,DomainId.408517" border="0" height="1" width="1" alt="Quantcast"/></div></noscript> <script>__ez.queue.addFile('/beardeddragon/drake.js', '/beardeddragon/drake.js?gcb=0&cb=4', false, [], true, false, true, false);</script></body></html> devs and subscribe to my newsletter