Question

How to mirror VueJS' logo properly in CSS-only?

I'm trying to replicate the VueJS logo with a CSS-only approach, not by importing an SVG or anything.

I've use clippy to have some proper clip-path, but still there is no way for me to find the proper rotation between the arms of the logo.


This is what I tried so far

.wrapper {
  height: 100svh;
  display: flex;
  flex-direction: row;
  align-items: center;
  transform: rotate(-60deg)
}

.wrapper div {
  width: 250px;
  height: 45px;
}

.left {
  transform: rotate(-60deg) translate(60%, 200%) scale(-1);
}

.green {
  background-color: #42b883;
  clip-path: polygon(33px 0, calc(100% - 33px) 0, 100% 100%, 0% 100%);
}

.blue {
  background-color: #35495e;
  clip-path: polygon(
    66px 0,
    calc(100% - 66px) 0,
    calc(100% - 33px) 100%,
    33px 100%
  );
}
<div class="wrapper">
  <main class="left">
    <div class="blue"></div>
    <div class="green"></div>
  </main>
  <main class="right">
    <div class="blue"></div>
    <div class="green"></div>
  </main>
</div>

I've tried various translations, transforms etc, but none of them seem to be logical, while I thought that applying a 60-degree rotation would be enough.
Turns out it totally didn't.

 2  233  2
1 Jan 1970

Solution

 10

You can get it using a single div, using just 2 gradients:

.vue {
    width: 500px;
    height: 500px;
    background-image: linear-gradient(60deg, transparent 54%, #00BD82 54%, #00BD82 70%, #3E5468 70%, #3E5468 85%, transparent 85%), linear-gradient(-60deg, transparent 54%, #00BD82 54%, #00BD82 70%, #3E5468 70%, #3E5468 85%, transparent 85%);
    background-size: 50% 100%, 50% 100%;
    background-position: 0 0, 100% 0;
    background-repeat: no-repeat;
    transform: scale(0.5);

}
<div class="vue"></div>

Another posibility, not using gradients, to avoid antialising:

The colors are set as the border and a shadow on a square div. The angle can bet get with a skew:

.vue {
    width: 140px;
    height: 140px;
    border: solid #00BD82;
    border-width: 0px 30px 30px 0px;
    box-shadow: inset -30px -30px #3E5468;
    transform: rotate(45deg) skew(15deg, 15deg);
    clip-path: polygon(0 100%, 100% 0, 100% 100%);
}
<div class="vue"></div>

2024-07-08
vals

Solution

 7

If you want to go with the clip-path technique, that works, but I might stick to only using clip-path, as opposed to trying to rotate and mirror your figure path after you've created it. clip-path is powerful enough to handle it all. Refer to this MDN page for information on how to use clip-path with polygon() - it's not overly difficult - it's just a comma separated list of coordinate pairs where each value can simply be defined as a percentage.

In the below example, I'm creating two divs, one for blue, and the other for green. I layer the two divs over each other and set their size to be the exact same.

/*
Layer the two divs over each other,
and set the size of the divs.
*/
.blue, .green {
  position: absolute;
  top: 0;
  left: 0;
  width: 512px;
  height: 444px;
}

.blue {
  background-color: #34495e;
  clip-path: polygon(
    20% 0%, /* top left */
    40% 0%,
    50% 23%, /* top of the inside of the V */
    60% 0%,
    80% 0%, /* top right */
    50% 60% /* bottom of the V */
  );
}

.green {
  background-color: #41b883;
  clip-path: polygon(
    0% 0%, /* top left */
    20% 0%,
    50% 60%, /* top of the inside of the V */
    80% 0%,
    100% 0%, /* top right */
    50% 100% /* bottom of the V */
  );
}
<div class="blue"></div>
<div class="green"></div>

If you instead want to go strong with the rotating/shifting stuff, here's how you might go about doing that (without using clip-path).

Step 1: Create a dark-blue div with a green bottom-border. Move and rotate it into position. The top will be cut off simply because it's shooting out above the top of the page.

/*
A rotated dark-blue box with a light-green border
under it. This is getting cropped by its #left-crop-container parent.
*/
#left-content {
  position: relative;
  background-color: #34495e;
  width: 550px;
  height: 82px;
  left: -72px;
  top: 96px;
  border-bottom: 89px solid #41b883;
  transform: rotate(60deg);
}
<div id="left-content"></div>

Step 2: Put our figure inside of a container. Give the container a fixed size, and configure it to crop anything that extrudes out of it (with overflow: hidden).

In the below example, I'll outline the box that does the cropping in red, so it's easier to see what's going on.

/*
The left half of the logo will be put inside of this box.
Anything that hangs out will be cropped via the `overflow: hidden` rule
*/
#left-crop-container {
  width: 256px;
  height: 445px;
  overflow: hidden;
  outline: solid red;
}

/*
A rotated dark-blue box with a light-green border
under it. This is getting cropped by its #left-crop-container parent.
*/
#left-content {
  position: relative;
  background-color: #34495e;
  width: 550px;
  height: 82px;
  left: -72px;
  top: 96px;
  border-bottom: 89px solid #41b883;
  transform: rotate(60deg);
}
<div id="left-crop-container">
  <div id="left-content"></div>
</div>

Step 3: Now we just need to mirror it to the other side. I'm going to accomplish this by copy-pasting the exact same HTML, but I'll put the pasted HTML inside of a container div that contains CSS to flip it.

/*
If the container is smaller than a logo
(e.g. because you're using a really small screen)
make sure the right half doesn't try
to wrap under the left half.
*/
#logo {
  text-wrap: nowrap;
}

/*
Makes the mirrored right half appear to
the right of the left half, instead of under it.
*/
#logo > * {
  display: inline-block
}

/*
The left half of the logo will be put inside of this box.
Anything that hangs out will be cropped via the `overflow: hidden` rule
*/
#left-crop-container {
  width: 256px;
  height: 445px;
  overflow: hidden;
}

/*
A rotated dark-blue box with a light-green border
under it. This is getting cropped by its #left-crop-container parent.
*/
#left-content {
  position: relative;
  background-color: #34495e;
  width: 550px;
  height: 82px;
  left: -72px;
  top: 96px;
  border-bottom: 89px solid #41b883;
  transform: rotate(60deg);
}

/* Mirrors its content, and shifts it slightly */
#mirror-contents-to-right {
  position: relative;
  transform: rotateY(180deg);
  left: -5px;
}
<div id="logo">
  <div id="left-crop-container">
    <div id="left-content"></div>
  </div>
  <div id="mirror-contents-to-right">
    <div id="left-crop-container">
      <div id="left-content"></div>
    </div>
  </div>
</div>

Ta-Dah!

2024-07-08
Scotty Jamison

Solution

 3

I did a bunch of CSS-only single element logos and VueJS is the first of the list.

Only 4 declarations and one value to control the size

.vue {
  width: 200px; /* control the size */
  aspect-ratio: 1.18;
  background: conic-gradient(from -30deg at 50% 60%, #34495e 60deg, #41b783 0);
  clip-path: polygon(0 0, 39% 0, 50% 23%, 61% 0, 100% 0, 50% 100%);
}
<div class="vue"></div>

2024-07-12
Temani Afif

Solution

 2

Do this with :before and :after + clip-path to them.
It's quite simple, but to make it adaptive - use aspect-ratio.
Here's an example where I've added the original svg next to it for comparison:

.logo {
  width: min(230px, 100%);
  aspect-ratio: 1.154;
  position: relative;
  &:before,
  &:after {
    content: '';
    position: absolute;
  }
  &:before {
    inset: 0 20% 40%;
    background-color: #34495e;
    clip-path: polygon(0 0, 30% 0, 50% 40%, 70% 0, 100% 0, 50% 100%);
  }
  &:after {
    inset: 0;
    background-color: #41b883;
    clip-path: polygon(0 0, 20% 0, 50% 60%, 80% 0, 100% 0, 50% 100%);
  }
}

/* Just for example */

body {
  display: flex;
  gap: 2px;
  margin: 0;
}

.logo-svg {
  width: min(230px, 100%);
  aspect-ratio: 1.154;
  display: flex;
  svg {
    width: 100%;
    height: 100%;
  }
}
<div class="logo"></div>
<div class="logo-svg">
  <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 261.76 226.69"><g transform="matrix(1.3333 0 0 -1.3333 -76.311 313.34)"><g transform="translate(178.06 235.01)"><path d="m0 0-22.669-39.264-22.669 39.264h-75.491l98.16-170.02 98.16 170.02z" fill="#41b883"/></g><g transform="translate(178.06 235.01)"><path d="m0 0-22.669-39.264-22.669 39.264h-36.227l58.896-102.01 58.896 102.01z" fill="#34495e"/></g></g></svg>
</div>

2024-07-10
imhvost