Ugly Sweaters with CSS

Want to see the code in action? Check out the codepen here.

CSS Grid

CSS Grid is positioning tool that allows us to take a high degree of control over the two-dimensional flow of items on a page. Other similar positioning tools you may have come across are CSS Float and CSS Flexbox. Float is the oldest and consequently most widely compatible positioning tool available. However, due to its many limitations in comparison to its successors, Float has fallen out of use in most modern web pages. Flexbox has many of the same advantages as Grid, but is primarily used for one-dimensional positioning of items on a page. This is an oversimplification of the similarities and differences between positioning tools, but for the purposes of this article, this is all we need to know. A quick Google search will unearth thousands of hours of videos that take an in depth look at the differences between the three if you’re curious.

.parent-element {
display: grid;
}

Clip-Path

The clip-path property creates a region that determines what portion of an element should be shown and what should remain hidden. Rather than a simple rectangle, we can use the clip-path property create unique shapes through which to see content inside an element. It’s important to know that we don’t “crop” the element, we only indicate what portion of the element should be displayed. This means that the clip-path does not affect the flow of content inside or outside of the element to which it’s assigned. Here are a few examples:

.parent-element {
clip-path: circle(100px at 10px 50px);
/* creates a circle with a radius of 100px positioned 10px from the left and 50px from the top */
clip-path: ellipse(15px 10px);
/* creates an ellipse with a width of 15px and a height of 10px positioned at the center */
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
/* creates a diamond shape where each set of values corresponds to a vertex positioned along the X and Y axis according to the percentages, starting from the top-left of the element */
}

Step 1: The Setup

In this step we’ll lay out the basic structure of what will eventually become the sweater.

<html>
<head></head>
<body>
<div>
<img src="" alt="">
<div>
</div>
</div>
</body>

</html>
<body class="page">
<div class="sweater">
<img src="" alt="" class="sweater__outline>
<div class="sweater__section-container>
</div>
</div>
</body>
Winter wonderland
Photo by Adam Chang on Unsplash
.page {  
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
background-image: url();
background-size: cover;
overflow: hidden;
}
.sweater {
width: 35vw;
height: 35vw;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}

Step 2: The Sweater

The next step is to find the shape and outline of our sweater. The shape is entirely up to you. To determine mine, I googled “sweater outline” and opted for something with a minimal design and a few curves. We want to make sure there is no white background on our outline. If you can’t find an outline with no background, use this tool to remove it yourself.

.sweater {
position: absolute;
height: 177%;
width: 177%;
z-index: 1;
}
Sweater outline with an overflowing red background
Currently our outline isn’t doing a great job of containing anything…
clip-path: polygon(50% 20%, 57% 20%, 60% 20%, 64% 22%, 74% 27%, 76%     29%, 78% 32%, 79% 36%, 86% 67%, 86% 69%, 85% 70%, 86% 74%, 78% 75%, 77% 70%, 77% 70%, 75% 67%, 70% 44%, 69% 38%, 69% 40%, 69% 70%, 68% 73%, 67% 74%, 66% 75%, 65% 78%, 65% 80%, 35% 80%, 34% 75%, 33% 75%, 33% 74%, 33% 74%, 32% 73%, 31% 71%, 31% 39%, 31% 39%, 30% 40%, 30% 43%, 23% 70%, 22% 71%, 22% 73%, 22% 75%, 22% 76%, 19% 76%, 17% 76%, 15% 75%, 13% 75%, 13% 72%, 15% 71%, 14% 70%, 13% 68%, 14% 65%, 23% 31%, 24% 29%, 25% 28%, 26% 26%, 29% 25%, 31% 24%, 33% 23%, 35% 22%, 37% 20%, 40% 19%, 41% 20%, 43% 19%, 46% 19%, 49% 20%, 52% 19%);
.sweater__section-container {
position: relative;
min-width: 230%;
height: 230%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
opacity: .7;
}

Step 3: The Sections

Now that we’ve created a container for our sweater design, it’s time to create the grid sections that will compose the design of our sweater.

.sweater__section-... {
display: grid;
overflow: hidden;
}
.sweater__section-top {
max-height: 30%;
}
.sweater__section-mid {
max-height: 15%;
}
.sweater__section-bot {
max-height: 55%;
}
.sweater__section-top {
display: grid;
overflow: hidden;
grid-auto-rows: 20px;
grid-template-columns: repeat(auto-fit, minmax(20px, 1fr));
grid-gap: 0px;
max-height: 30%;
}
.sweater__section-mid {
display: grid;
overflow: hidden;
grid-auto-rows: 40px;
grid-template-columns: repeat(auto-fit, minmax(30px, 1fr));
grid-gap: 3px;
max-height: 15%;
}
.sweater__section-bot {
display: grid;
overflow: hidden;
grid-auto-rows: 200px;
grid-template-columns: repeat(auto-fit, minmax(50px, 1fr));
grid-gap: 10px;
max-height: 55%;
}
.sweater__section-top {
display: grid;
overflow: hidden;
grid-auto-rows: 20px;
grid-template-columns: repeat(auto-fit, minmax(20px, 1fr));
grid-gap: 0px;
max-height: 30%;
background-color: #4D0000;
}
.sweater__section-mid {
display: grid;
overflow: hidden;
grid-auto-rows: 40px;
grid-template-columns: repeat(auto-fit, minmax(30px, 1fr));
grid-gap: 3px;
max-height: 15%;
background-color: #4D0000;
border: 3px solid #4D0000;
}
.sweater__section-bot {
display: grid;
overflow: hidden;
grid-auto-rows: 200px;
grid-template-columns: repeat(auto-fit, minmax(50px, 1fr));
grid-gap: 10px;
max-height: 55%;
background-color: #4D0000;
}
Where are all those cells we just configured?

Step 4: The Cells

Now in each section we have around 100 cells, but each of those cells have the same class assigned to them. It might seem that we’re either going to need to apply color to each cell individually with new classes, one by one, cell by cell, over and over or we’re going to have to settle for assigning a single background-color to that one class and calling a day.

.sweater__cell:nth-child(5n) {
background-color: #FFCDB2;
}
.sweater__cell:nth-child(5n+1) {
background-color: #FFB4A2;
}
.sweater__cell:nth-child(5n+2) {
background-color: #E5989B;
}
.sweater__cell:nth-child(5n+3) {
background-color: #B5838D;
}
.sweater__cell:nth-child(5n+4) {
background-color: #6D6875;
}
.sweater__section-mid .knit:nth-child(odd) {
clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}
.sweater__section-mid .knit:nth-child(even) {
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}
.sweater__section-bot .knit {
clip-path: polygon(10% 25%, 35% 25%, 35% 0%, 65% 0%, 65% 25%, 90% 25%, 90% 50%, 65% 50%, 65% 100%, 35% 100%, 35% 50%, 10% 50%);
}
For example…

Step 5: The Spice

After some tinkering

In Summary

In this project we took a brief look at a few important topics:

  • How to use grid-template-columns to automatically determine the amount and size of columns in a grid.
  • How to use clip-paths to determine what content within an element should and should not be displayed.
  • How using the :nth-child() pseudo-selector allows us to select specific siblings amongst a group of many siblings.
  • How to use descendant combinators to select only descendants of a specific ancestor.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Jake McCambley

Jake McCambley

Learning to code by teaching others — Living at the cross section of tech, music, and the outdoors — Currently studying Web Development at Practicum by Yandex.