3

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.

1
  • I have posted another snippet, to avoid the antialiasing in the background techniques Commented Jul 19, 2024 at 14:50

4 Answers 4

11

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. We can get the angle with a skew transform:

.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>

Sign up to request clarification or add additional context in comments.

Comments

8

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!

1 Comment

Thanks for the effort and the detailed explanations. clip-path is indeed powerful enough, thanks for that reminder!
4
+200

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>

7 Comments

A gradient is a good solution. But it is ribbed )) This can be seen both in your answer and in the answer of @vals. So we have to anti-aliasing - and it's annoying.
@imhvost I cannot see any AA (or the need for it) so far on any answers. Even at 500% + fully zoomed close by, good enough result for me. Might be coming from your OS/system settings.
But the ribbing is visible prnt.sc/kbAl1_jjIS1A
@imhvost I definitely do not have that one on my side.
Very nice and short answer. The whole clip-path + conic-gradient + aspect-ratio are quite impressive. It's also quite easy to change the size of it. Overall, such a thing should not be handled with the tools I tried initially haha! PS: just realized I'm following you on Twitter already, makes sense regarding the cool CSS projects you're shipping Thanks man!
|
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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.