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