Creating a Vintage Television using BEM, CSS3, and JavaScript
Many graphic effects that were previously only possible using a graphics editor are now feasible thanks to a combination of simple CSS rules. This trend is often referred to as “CSS Art”, a term that indicates the use of CSS for the creation of graphic experiments where the CSS bosses the show — whether it be images or real animations.
A few beautiful examples of CSS art that you may have seen include Homer Simpson and Darth Vader, both created with pure CSS.
In this article, I’ll show you how to draw a small vintage television created using HTML5 and CSS3 with the addition of few lines of JavaScript to simulate the possibility to show different channels (actually videos taken from YouTube).
It should be noted that although a user may be attracted by these effects, probably in some cases they are taken to extremes. We would always want to ensure that any technique we use isn’t affecting the performance or maintainability of our code.
Here’s a CodePen demo showing what we’ll be creating:
See the Pen CSS3 and JavaScript Vintage TV by SitePoint (@SitePoint) on CodePen.
A Brief BEM Intro
For this project we’ll use the BEM methodology for naming our CSS classes.
Explaining BEM in great detail is outside the scope of this article, but here I’ll cover it briefly.
The acronym BEM stands for Block Element Modifier. This methodology suggests a structured way of naming classes, based on the properties and role of the element in question. To adhere to the BEM methodology, we have to stick with a naming convention that follows this pattern:
block
represents the higher level of an abstraction or component.block__element
represents a descendent of block that helps formblock
as a whole.block–modifier
represents a different state or version ofblock
.
BEM lets us to be more descriptive and explicit since we are able to understand relations with other elements of our code through naming. By reading the HTML code with the classes assigned, you can see how the chunks (blocks, elements, variation or modifier of those elements) are related and how they work.
Let’s see how BEM works; then, we’ll see it applied to our television.
For a quick example, think about how the following elements are related and how they are named:
<article class="book">
<img src="/path/to/image.jpg" class="book__cover book__cover--large" />
<h1 class="book__title">The little prince</h1>
</article>
That should give you a good intro to what BEM is. For a deeper discussion, check out Harry Roberts’ article. Now let’s move on to the code for our television.
The HTML for the Television
The markup underpinning the television is, as is the case with much CSS art, fairly deep, but not too difficult to grasp when looking at the BEM-based class names:
<div class="television">
<div class="television__top">
<div class="television__antenna television__antenna--left"></div>
<div class="television__antenna television__antenna--right"></div>
<div class="television__antenna__base"></div>
</div><!-- television__top -->
<div class="television__center">
<div class="television__screen">
<iframe src="" frameborder="0" allowfullscreen></iframe>
</div><!-- television__screen -->
<div class="television__channels-wrapper">
<ul class="television__channels">
<li class="television__channel"><a href="http://www.youtube.com/embed/SRu6YRr1KtM" title="Channel 1"></a></li>
<li class="television__channel"><a href="http://www.youtube.com/embed/oRdxUFDoQe0" title="Channel 2"></a></li>
<li class="television__channel"><a href="http://www.youtube.com/embed/EGikhmjTSZI" title="Channel 3"></a></li>
<li class="television__channel"><a href="http://www.youtube.com/embed/06qJVpUSKZY" title="Channel 4"></a></li>
<li class="television__channel"><a href="http://www.youtube.com/embed/v_09wFxoaeQ" title="Channel 5"></a></li>
<li class="television__channel"><a href="http://www.youtube.com/embed/Tj75Arhq5ho" title="Channel 6"></a></li>
</ul>
</div><!-- television__channels-wrapper -->
</div><!-- television__center -->
<div class="television__base">
<div class="television__foot television__foot--left"></div>
<div class="television__foot television__foot--right"></div>
</div><!-- television__base -->
</div><!-- television -->
Here’s a quick breakdown of what we’ve done here in the HTML:
- There are three main sections inside the .television container: top, center, and base.
- The top section is what holds the antenna parts
- The center holds the screen and the channels
- The base holds the feet of the TV
Notice that these three main sections, as well as the inner elements are named using a double underscore, which, in BEM, represents an element of the higher level block.
Next to some of these classes, (for example, when constructing the antenna) we have names like television__antenna--left
and television__antenna--right
. The reason for such names is that while the element is an antenna, we have to modify the element to act as the left or the right antenna respectively. So, the final part of the name suggests that all the rules we’ll apply to that particular class will define its style. In this case, they’ll define the style of the left or the right antenna of our vintage television.
The question is not so hard to understand and this example should reinforce the concepts explained while discussing BEM. There is a block which can contain one or more elements and these descendent elements can have one or more specific characteristics, intended as modifiers. The remainder of the HTML follows a similar BEM pattern.
The television__screen
element has is its only child, the iframe
, that we’ll use to display the videos loaded from YouTube. The television__channels--wrapper
contains an unordered list (<ul>
), which will hold the videos we want to be played when one of the six channel buttons is clicked. The href
attribute will be used to modify the load the intended video, which we’ll get to later.
Note also that in this project we’ve used <div>
s because we don’t need tags with any kind of semantic meaning.
Now it’s time to have move on to our CSS.
The CSS for our Television
In this section, we’ll only consider the relevant portions of the CSS, so some generic rules have been left out of the code here. Let’s take a look:
iframe {
box-sizing: border-box;
width: 100%;
height: 100%;
overflow: hidden;
border: 10px solid;
border-radius: 32px;
}
.television {
width: 450px;
margin: 0 auto;
}
.television__top {
width: 40%;
margin: auto;
position: relative;
}
.television__antenna {
width: 5px;
height: 100px;
background-color: #3b3733;
margin-bottom: -10px;
}
.television__antenna--left {
transform: rotate(-30deg);
float: left;
}
.television__antenna--right {
transform: rotate(30deg);
float: right;
}
.television__antenna__base {
height: 20px;
background-color: #3b3733;
border-top-left-radius: 48px;
border-top-right-radius: 48px;
margin-bottom: 10px;
clear: both;
position: relative;
z-index: 2;
}
Here’s a point by point summary of the important aspects of the CSS:
- We’ve used the
transform
property to position the antenna parts, one rotated clockwise, the other counter-clockwise. - A negative margin and the
float
property help us establish the position of the antenna parts in relation to the TV, and to each other. - We further use
clear
andz-index
to ensure the float doesn’t cause problems to the layout of subsequent elements and the antenna elements are stacked correctly. - To ensure the iframe looks ‘rounded’ in all browsers, we use a border trick.
Let’s now look at the code for the center part of the television:
.television__center {
position: relative;
z-index: 2;
width: 350px;
height: 200px;
background-color: #7a4e27;
border: solid 3px #eef4c4;
box-shadow: -10px -10px #d64832;
border-radius: 48px;
margin: 0 auto;
padding: 10px 15px;
}
.television__screen {
width: 270px;
height: 190px;
background-color: #eed5b6;
border-radius: 48px;
float: left;
}
.television__channels-wrapper {
width: 70px;
height: 190px;
background-color: #efd6b7;
border-radius: 48px;
float: right;
}
.television__channels {
padding: 0;
}
.television__channel {
list-style-type: none;
background-color: #7f4b23;
width: 25px;
height: 25px;
border-radius: 50%;
margin: 5px;
float: left;
position: relative;
}
.television__channel a {
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #e6a146;
width: 12px;
height: 12px;
border-radius: 50%;
}
.television__channel a:active {
box-shadow: inset rgba(255,255,255,0.6) 0 2px 2px,
inset rgba(0,0,0,0.15) 0 -2px 5px, /* inner shadow */
rgba(100,100,100,0.6) 0 2px 1px,
rgba(100,100,100,0.6) 0 2px 1px; /* color border */
}
Here are the main points from the code above:
z-index
is used to correctly stack the TV over the feet- The “vintage” effect for our television is created by a combination of shadows, borders, colors and padding.
- The channel buttons are created with a 50%
border-radius
, which creates a circle at any element size. - The channel buttons are individually centered vertically and horizontally and vertically using
transform: translate(-50%, -50%)
. - We use the
:active
pseudo-class to style the ‘clicked’ state of the channels, using shadows and borders to create the effect.
Finally, let’s look at the last section of our CSS, shown below.
.television__base {
width: 60%;
margin: auto;
}
.television__base:after {
clear: both;
content: "";
display: table;
}
.television__foot {
width: 20px;
height: 70px;
margin-top: -20px;
background-color: #7d4d25;
border: solid 3px #eed5b6;
border-radius: 48px;
}
.television__foot--left {
transform: rotate(30deg);
float: left;
}
.television__foot--right {
transform: rotate(-30deg);
float: right;
}
And here’s a description of the key parts of the above code:
- We use a clearfix method to fix a float/clearing issue.
- We’ve applied the same rotation of 30 degrees of the antennas also to the feet of the television, along with
float: left
andfloat: right
to bring them closer.
The JavaScript to Make the Channels Work
The last section of this article focuses on the small JavaScript code that enables the demo to show some videos when the channel button are pressed. With the iframe
element in place in our markup, loading videos from YouTube is very easy. Our goal is to attach a handler to be executed when a click is performed on a button of the panel.
Here is the code:
var buttons = document.querySelectorAll('.television__channel a');
for(var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function(event) {
document.querySelector('.television__screen iframe').src = this.href;
event.preventDefault();
});
}
Here we use querySelectorAll
select the buttons, that is the a
elements inside the list items having a class of .television__channel
, which in a variable called buttons
. Then we iterate over each element retrieved to attach a handler using the addEventListener()
method to run when the click
event is triggered on it.
Inside the click handler we have to perform only two actions. The first is to set that very URL as the src
of the iframe
element. The second one is to prevent the default action, that in this case is to load the page specified by the URL of the href
attribute. With these lines in place, the browser will load the video from YouTube ready to be played. Cool, isn’t it?
Demo and Conclusion
Once again, here is the demo display our working TV:
See the Pen CSS3 and JavaScript Vintage TV by SitePoint (@SitePoint) on CodePen.
As mentioned in the introduction, these types of CSS experiments aren’t always the most practical way to create graphics. This is more a way to see what’s possible with CSS, and maybe help you learn some techniques along the way.
And although we could have used pseudo-elements to produce many of the elements, it might in fact be easier to do it all in the HTML, so it’s clear where our different elements are placed.