Back button to all articlesAll articles

Developing the Star Wars opening crawl in HTML/CSS

To celebrate Star Wars: The Last Jedi coming to theaters I decided to implement a fun easter egg on my personal website.

I wanted to display the saga's famous opening crawl when a user inputs the Konami Code.

This blog post will show every step that was necessary to achieve it, from building it in HTML/CSS, to implementing it in an Angular project and adding final touches such as John Williams' famous track.

First step: Catching the Konami Code

I decided to catch my user's input from the root component of my website: the AppComponent.

I headed over to app.component.ts and added the following:

1// The Konami Code itself 2konamiCode = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a']; 3 4// Our user's position in the code 5konamiCodePosition = 0; 6 7// True once the code is valid, false otherwise 8validCode = false; 9 10// Catch keydown events 11@HostListener('document:keydown', ['$event']) 12handleKeyboardEvent(event: KeyboardEvent) { 13 var requiredKey = this.konamiCode[this.konamiCodePosition]; 14 15 // If the input equals to the required key 16 if (event.key == requiredKey) { 17 // Go to the next step of the Konami code 18 this.konamiCodePosition++; 19 20 // If we are at the end of it, validate the code and reset the position to 0 21 if (this.konamiCodePosition == this.konamiCode.length) { 22 this.validCode = true; 23 this.konamiCodePosition = 0; 24 } 25 } else { 26 // The input was false, set the validity to false and the position to 0 27 this.validCode = false; 28 this.konamiCodePosition = 0; 29 } 30} 31

Pretty simple, huh? This could be done in many other and better ways, but that will do the trick.

Second step: Displaying the right component

Once the code has been validated, I wish to display the component that will contain the crawl itself.

Firstly, I create the new component:
ng g component star-wars

Then I use the Angular directive *ngIf to hide or display it.

In app.component.html:

1<div class="parent" *ngIf="!validCode"> 2 <router-outlet></router-outlet> 3</div> 4 5<app-star-wars *ngIf="validCode"></app-star-wars> 6

As you can see, if the code is invalid we display the website's usual content (the router-outlet) otherwise we display our newly created component !

Third step: Creating the crawl

This was tricky at first, but once you break it down into several parts it becomes easy to understand.

The crawl contains 5 essential elements:

  • The background
  • The intro phrase
  • The logo
  • The text
  • The music

All of these steps take place in the newly created StarWarsComponent.

Creating the background

Let's do something a bit more original than adding a picture of space. Let's generate stars randomly !

In star-wars.component.html:

1<div id="space"></div> 2

And in star-wars.component.ts:

1// Sets the number of stars we wish to display 2readonly numStars = 100; 3 4// Inject what we need to access the native document variable 5constructor(@Inject(DOCUMENT) private document: any) { } 6 7ngOnInit() { 8 // For every star we want to display 9 for (let i = 0; i < this.numStars; i++) { 10 // Create a new element 11 let star = this.document.createElement("div"); 12 // Set its style to resemble a star 13 star.style.position = "absolute"; 14 star.style.width = "1px"; 15 star.style.height = "1px"; 16 star.style.backgroundColor = "white"; 17 // Get random positions on the screen and set them 18 var xy = this.getRandomPosition(); 19 star.style.top = xy[0] + 'px'; 20 star.style.left = xy[1] + 'px'; 21 // Append our new star 22 this.document.getElementById("space").append(star); 23 } 24} 25 26// Gets random x, y values based on the size of the container 27getRandomPosition() { 28 var y = window.innerWidth; 29 var x = window.innerHeight; 30 var randomX = Math.floor(Math.random()*x); 31 var randomY = Math.floor(Math.random()*y); 32 return [randomX,randomY]; 33} 34

And finally in star-wars.component.sass:

1#space 2 background-color: black 3 width: 100% 4 height: 100% 5 position: absolute 6

Tada ! We now have a beautiful background to display the crawl on.

It looks like this (note that the stars might not be visible on these pictures as they are a single pixel wide):

Adding the famous intro phrase

A long time ago in a galaxy far, far away....

Everyone has already heard, seen or whispered this phrase in their lifetime, so let's add it to our component (with the necessary effects).

In star-wars.component.html:

1... 2 3<section class="intro"> 4 A long time ago, in a galaxy far,<br /> 5 far away.... 6</section> 7

In star-wars.component.sass:

1... 2 3// Center the section element 4section 5 position: absolute 6 top: 45% 7 left: 50% 8 z-index: 1 9 10// Set the animation, color, size and hide the text 11.intro 12 animation: intro 6s ease-out 1s 13 margin: 0 0 0 (- 15em / 2) 14 color: rgb(75, 213, 238) 15 font-weight: 400 16 font-size: 300% 17 width: 15em 18 opacity: 0 19 20@keyframes intro 21 0% 22 opacity: 0 23 20% 24 opacity: 1 25 90% 26 opacity: 1 27 100% 28 opacity: 0 29

Result:

Displaying the logo

The logo is vital to this opening sequence, here's how I added it.

In star-wars.component.html:

1... 2 3<section class="logo"> 4 <!-- SVG GOES HERE --> 5</section> 6

The SVG being a very long file, I have uploaded it here for you to copy and paste.

In star-wars.component.sass:

1... 2 3// Set the animation & hide the logo 4.logo 5 animation: logo 9s ease-out 9s 6 margin: 0 0 0 (- 18em / 2) 7 opacity: 0 8 9 svg 10 width: inherit 11 12// Scale the logo down and maintain it centered 13@keyframes logo 14 0% 15 width: 18em 16 margin: (- 18em / 2) 0 0 (- 18em / 2) 17 transform: scale(2.75) 18 opacity: 1 19 50% 20 opacity: 1 21 width: 18em 22 margin: (- 18em / 2) 0 0 (- 18em / 2) 23 100% 24 opacity: 0 25 transform: scale(0.1) 26 width: 18em 27 margin: (- 18em / 2) 0 0 (- 18em / 2) 28

And there we go, our beautifully animated logo:

Adding the scrolling text

It's probably the most essential part of the crawl but it's rather easy to implement.

In star-wars.component.html:

1... 2 3<!-- Change the text to your liking --> 4<div id="board"> 5 <div id="content"> 6 <p id="title">Episode I</p> 7 <p id="subtitle">THE CODER'S MENACE</p> 8 <br /> 9 <!-- And make it cheesy ! --> 10 <p> 11 Turmoil has engulfed the Galactic Republic as Christopher Kade finishes 12 studying to become a master in his trade. 13 </p> 14 <p> 15 Hoping to resolve the matter with side-projects and research, he retreated 16 to the small planet of Ireland for the coming year. 17 </p> 18 <p> 19 As his skills keep on evolving through constant learnings, his passion for 20 open-source technologies grows with it... 21 </p> 22 </div> 23</div> 24

In star-wars.component.sass:

1... 2 3p 4 color: #FFFF82 5 6// Set the font, lean the board, position it 7#board 8 font-family: Century Gothic, CenturyGothic, AppleGothic, sans-serif 9 transform: perspective(300px) rotateX(25deg) 10 transform-origin: 50% 100% 11 text-align: justify 12 position: absolute 13 margin-left: -9em 14 font-weight: bold 15 overflow: hidden 16 font-size: 350% 17 height: 50em 18 width: 18em 19 bottom: 0 20 left: 50% 21 &:after 22 background-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, transparent 100%) 23 pointer-events: none 24 position: absolute 25 content: ' ' 26 bottom: 60% 27 left: 0 28 right: 0 29 top: 0 30 31// Set the scrolling animation and position it 32#content 33 animation: scroll 100s linear 16s 34 position: absolute 35 top: 100% 36 37#title, #subtitle 38 text-align: center 39 40@keyframes scroll 41 0% 42 top: 100% 43 100% 44 top: -170% 45

And there we go !

Final touch: the music

What would Star Wars be without its music?

Since we have timed our animations in advance, it should be a piece of cake !

First, download the following .mp3 file and add it to your project's assets.

Then, in our html file, add:

1<audio preload="auto" autoplay> 2 <source 3 src="./../../assets/audio/Star_Wars_original_opening_crawl_1977.mp3" 4 type="audio/mpeg" 5 /> 6</audio> 7

Which preloads the music as the page is loaded and plays it automatically.

And voilà, everything should work as expected.

Final thoughts

You can check out the final product on my website by inputing the Konami Code (OUTDATED). It really was a blast to develop and I hope it shows how much possibilities you have with such a basic kit.

Thanks for reading,
@christo_kade