Add metadata images to meta folder for consistency
Add metadata images to meta folder for consistency

file:b/CONTRIBUTING.md (new)
  ## Contributing
  Changes and improvements are more than welcome! Feel free to fork and open a pull request. Please make your changes in a specifically made branch and request to pull on `master`! If you can, please make sure the game fully works before sending the PR, as that will help speed up the process.
 
file:a/README.md -> file:b/README.md
# 2048 # 2048
A small clone of [1024](https://play.google.com/store/apps/details?id=com.veewo.a1024), based on [Saming's 2048](saming.fr/p/2048/) (also a clone). A small clone of [1024](https://play.google.com/store/apps/details?id=com.veewo.a1024), based on [Saming's 2048](http://saming.fr/p/2048/) (also a clone).
   
Made just for fun. [Play it here!](http://gabrielecirulli.github.io/2048/) Made just for fun. [Play it here!](http://gabrielecirulli.github.io/2048/)
   
[![Screenshot](http://pictures.gabrielecirulli.com/2048-20140309-234100.png)](http://pictures.gabrielecirulli.com/2048-20140309-234100.png) [![Screenshot](http://pictures.gabrielecirulli.com/2048-20140309-234100.png)](http://pictures.gabrielecirulli.com/2048-20140309-234100.png)
   
  That screenshot is fake, by the way. I never reached 2048 :smile:
   
  ## Contributing
  Changes and improvements are more than welcome! Feel free to fork and open a pull request. Please make your changes in a specific branch and request to pull into `master`! If you can, please make sure the game fully works before sending the PR, as that will help speed up the process.
   
  You can find the same information in the [contributing guide.](https://github.com/gabrielecirulli/2048/blob/master/CONTRIBUTING.md)
   
  ## License
  2048 is licensed under the [MIT license.](https://github.com/gabrielecirulli/2048/blob/master/LICENSE.txt)
   
  ## Donations
  I made this in my spare time, and it's hosted on GitHub (which means I don't have any hosting costs), but if you enjoyed the game and feel like buying me coffee, you can donate at my BTC address: `1Ec6onfsQmoP9kkL3zkpB6c5sA4PVcXU2i`. Thank you very much!
   
file:b/favicon.ico (new)
 Binary files /dev/null and b/favicon.ico differ
file:a/index.html -> file:b/index.html
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>2048</title> <title>2048</title>
   
<link href="style/main.css" rel="stylesheet" type="text/css"> <link href="style/main.css" rel="stylesheet" type="text/css">
  <link rel="shortcut icon" href="favicon.ico">
  <link rel="apple-touch-icon" href="/meta/apple-touch-icon.png">
  <meta name="apple-mobile-web-app-capable" content="yes">
   
<script src="js/keyboard_input_manager.js"></script> <meta name="HandheldFriendly" content="True">
<script src="js/html_actuator.js"></script> <meta name="MobileOptimized" content="320">
<script src="js/grid.js"></script> <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0, maximum-scale=1, user-scalable=no">
<script src="js/tile.js"></script>  
<script src="js/game_manager.js"></script>  
<script src="js/application.js"></script>  
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<div class="heading"> <div class="heading">
<h1 class="title">2048</h1> <h1 class="title">2048</h1>
<div class="score-container">0</div> <div class="score-container">0</div>
</div> </div>
<p class="game-intro">Join the numbers and get to the <strong>2048 tile!</strong></p> <p class="game-intro">Join the numbers and get to the <strong>2048 tile!</strong></p>
   
<div class="game-container"> <div class="game-container">
<div class="game-message"> <div class="game-message">
<p></p> <p></p>
<div class="lower"> <div class="lower">
<a class="retry-button">Try again</a> <a class="retry-button">Try again</a>
</div> </div>
</div> </div>
   
<div class="grid-container"> <div class="grid-container">
<div class="grid-row"> <div class="grid-row">
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
</div> </div>
<div class="grid-row"> <div class="grid-row">
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
</div> </div>
<div class="grid-row"> <div class="grid-row">
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
</div> </div>
<div class="grid-row"> <div class="grid-row">
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
<div class="grid-cell"></div> <div class="grid-cell"></div>
</div> </div>
</div> </div>
   
<div class="tile-container"> <div class="tile-container">
   
</div> </div>
</div> </div>
   
<p class="game-explanation"> <p class="game-explanation">
<strong class="important">How to play:</strong> Use your <strong>arrow keys</strong> to move the tiles. When two tiles with the same number touch, they <strong>merge into one!</strong> <strong class="important">How to play:</strong> Use your <strong>arrow keys</strong> to move the tiles. When two tiles with the same number touch, they <strong>merge into one!</strong>
</p> </p>
<hr> <hr>
<p> <p>
Created by <a href="http://gabrielecirulli.com" target="_blank">Gabriele Cirulli.</a> Based on <a href="https://itunes.apple.com/us/app/1024!/id823499224" target="_blank">1024 by Veewo Studio.</a> Created by <a href="http://gabrielecirulli.com" target="_blank">Gabriele Cirulli.</a> Based on <a href="https://itunes.apple.com/us/app/1024!/id823499224" target="_blank">1024 by Veewo Studio</a> and conceptually similar to <a href="http://asherv.com/threes/" target="_blank">Threes by Asher Vollmer.</a>
</p> </p>
</div> </div>
   
  <script src="js/animframe_polyfill.js"></script>
  <script src="js/keyboard_input_manager.js"></script>
  <script src="js/html_actuator.js"></script>
  <script src="js/grid.js"></script>
  <script src="js/tile.js"></script>
  <script src="js/game_manager.js"></script>
  <script src="js/application.js"></script>
</body> </body>
</html> </html>
   
  (function() {
  var lastTime = 0;
  var vendors = ['webkit', 'moz'];
  for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
  window.cancelAnimationFrame =
  window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
  }
 
  if (!window.requestAnimationFrame) {
  window.requestAnimationFrame = function(callback, element) {
  var currTime = new Date().getTime();
  var timeToCall = Math.max(0, 16 - (currTime - lastTime));
  var id = window.setTimeout(function() { callback(currTime + timeToCall); },
  timeToCall);
  lastTime = currTime + timeToCall;
  return id;
  };
  }
 
  if (!window.cancelAnimationFrame) {
  window.cancelAnimationFrame = function(id) {
  clearTimeout(id);
  };
  }
  }());
 
document.addEventListener("DOMContentLoaded", function () { // Wait till the browser is ready to render the game (avoids glitches)
// Wait till the browser is ready to render the game (avoids glitches) window.requestAnimationFrame(function () {
window.requestAnimationFrame(function () { var manager = new GameManager(4, KeyboardInputManager, HTMLActuator);
var manager = new GameManager(4, KeyboardInputManager, HTMLActuator);  
});  
}); });
   
function HTMLActuator() { function HTMLActuator() {
this.tileContainer = document.getElementsByClassName("tile-container")[0]; this.tileContainer = document.getElementsByClassName("tile-container")[0];
this.scoreContainer = document.getElementsByClassName("score-container")[0]; this.scoreContainer = document.getElementsByClassName("score-container")[0];
this.messageContainer = document.getElementsByClassName("game-message")[0]; this.messageContainer = document.getElementsByClassName("game-message")[0];
   
this.score = 0; this.score = 0;
} }
   
HTMLActuator.prototype.actuate = function (grid, metadata) { HTMLActuator.prototype.actuate = function (grid, metadata) {
var self = this; var self = this;
   
window.requestAnimationFrame(function () { window.requestAnimationFrame(function () {
self.clearContainer(self.tileContainer); self.clearContainer(self.tileContainer);
   
grid.cells.forEach(function (column) { grid.cells.forEach(function (column) {
column.forEach(function (cell) { column.forEach(function (cell) {
if (cell) { if (cell) {
self.addTile(cell); self.addTile(cell);
} }
}); });
}); });
   
self.updateScore(metadata.score); self.updateScore(metadata.score);
   
if (metadata.over) self.message(false); // You lose if (metadata.over) self.message(false); // You lose
if (metadata.won) self.message(true); // You win! if (metadata.won) self.message(true); // You win!
}); });
}; };
   
HTMLActuator.prototype.restart = function () { HTMLActuator.prototype.restart = function () {
this.clearMessage(); this.clearMessage();
}; };
   
HTMLActuator.prototype.clearContainer = function (container) { HTMLActuator.prototype.clearContainer = function (container) {
while (container.firstChild) { while (container.firstChild) {
container.removeChild(container.firstChild); container.removeChild(container.firstChild);
} }
}; };
   
HTMLActuator.prototype.addTile = function (tile) { HTMLActuator.prototype.addTile = function (tile) {
var self = this; var self = this;
   
var element = document.createElement("div"); var element = document.createElement("div");
var position = tile.previousPosition || { x: tile.x, y: tile.y }; var position = tile.previousPosition || { x: tile.x, y: tile.y };
positionClass = this.positionClass(position); positionClass = this.positionClass(position);
   
// We can't use classlist because it somehow glitches when replacing classes // We can't use classlist because it somehow glitches when replacing classes
var classes = ["tile", "tile-" + tile.value, positionClass]; var classes = ["tile", "tile-" + tile.value, positionClass];
this.applyClasses(element, classes); this.applyClasses(element, classes);
   
element.textContent = tile.value; element.textContent = tile.value;
   
if (tile.previousPosition) { if (tile.previousPosition) {
// Make sure that the tile gets rendered in the previous position first // Make sure that the tile gets rendered in the previous position first
window.requestAnimationFrame(function () { window.requestAnimationFrame(function () {
classes[2] = self.positionClass({ x: tile.x, y: tile.y }); classes[2] = self.positionClass({ x: tile.x, y: tile.y });
self.applyClasses(element, classes); // Update the position self.applyClasses(element, classes); // Update the position
}); });
} else if (tile.mergedFrom) { } else if (tile.mergedFrom) {
classes.push("tile-merged"); classes.push("tile-merged");
this.applyClasses(element, classes); this.applyClasses(element, classes);
   
// Render the tiles that merged // Render the tiles that merged
tile.mergedFrom.forEach(function (merged) { tile.mergedFrom.forEach(function (merged) {
self.addTile(merged); self.addTile(merged);
}); });
} else { } else {
classes.push("tile-new"); classes.push("tile-new");
this.applyClasses(element, classes); this.applyClasses(element, classes);
} }
   
// Put the tile on the board // Put the tile on the board
this.tileContainer.appendChild(element); this.tileContainer.appendChild(element);
}; };
   
HTMLActuator.prototype.applyClasses = function (element, classes) { HTMLActuator.prototype.applyClasses = function (element, classes) {
element.setAttribute("class", classes.join(" ")); element.setAttribute("class", classes.join(" "));
}; };
   
HTMLActuator.prototype.normalizePosition = function (position) { HTMLActuator.prototype.normalizePosition = function (position) {
return { x: position.x + 1, y: position.y + 1 }; return { x: position.x + 1, y: position.y + 1 };
}; };
   
HTMLActuator.prototype.positionClass = function (position) { HTMLActuator.prototype.positionClass = function (position) {
position = this.normalizePosition(position); position = this.normalizePosition(position);
return "tile-position-" + position.x + "-" + position.y; return "tile-position-" + position.x + "-" + position.y;
}; };
   
HTMLActuator.prototype.updateScore = function (score) { HTMLActuator.prototype.updateScore = function (score) {
this.clearContainer(this.scoreContainer); this.clearContainer(this.scoreContainer);
   
var difference = score - this.score; var difference = score - this.score;
this.score = score; this.score = score;
   
this.scoreContainer.textContent = this.score; this.scoreContainer.textContent = this.score;
   
if (difference > 0) { if (difference > 0) {
var addition = document.createElement("div"); var addition = document.createElement("div");
addition.classList.add("score-addition"); addition.classList.add("score-addition");
addition.textContent = "+" + difference; addition.textContent = "+" + difference;
   
this.scoreContainer.appendChild(addition); this.scoreContainer.appendChild(addition);
} }
}; };
   
HTMLActuator.prototype.message = function (won) { HTMLActuator.prototype.message = function (won) {
var type = won ? "game-won" : "game-over"; var type = won ? "game-won" : "game-over";
var message = won ? "You win!" : "Game over!" var message = won ? "You win!" : "Game over!"
   
  // if (ga) ga("send", "event", "game", "end", type, this.score);
   
this.messageContainer.classList.add(type); this.messageContainer.classList.add(type);
this.messageContainer.getElementsByTagName("p")[0].textContent = message; this.messageContainer.getElementsByTagName("p")[0].textContent = message;
}; };
   
HTMLActuator.prototype.clearMessage = function () { HTMLActuator.prototype.clearMessage = function () {
this.messageContainer.classList.remove("game-won", "game-over"); this.messageContainer.classList.remove("game-won", "game-over");
}; };
   
function KeyboardInputManager() { function KeyboardInputManager() {
this.events = {}; this.events = {};
   
this.listen(); this.listen();
} }
   
KeyboardInputManager.prototype.on = function (event, callback) { KeyboardInputManager.prototype.on = function (event, callback) {
if (!this.events[event]) { if (!this.events[event]) {
this.events[event] = []; this.events[event] = [];
} }
this.events[event].push(callback); this.events[event].push(callback);
}; };
   
KeyboardInputManager.prototype.emit = function (event, data) { KeyboardInputManager.prototype.emit = function (event, data) {
var callbacks = this.events[event]; var callbacks = this.events[event];
if (callbacks) { if (callbacks) {
callbacks.forEach(function (callback) { callbacks.forEach(function (callback) {
callback(data); callback(data);
}); });
} }
}; };
   
KeyboardInputManager.prototype.listen = function () { KeyboardInputManager.prototype.listen = function () {
var self = this; var self = this;
   
var map = { var map = {
38: 0, // Up 38: 0, // Up
39: 1, // Right 39: 1, // Right
40: 2, // Down 40: 2, // Down
37: 3 // Left 37: 3, // Left
  75: 0, // vim keybindings
  76: 1,
  74: 2,
  72: 3,
  87: 0, // W
  68: 1, // D
  83: 2, // S
  65: 3 // A
}; };
   
document.addEventListener("keydown", function (event) { document.addEventListener("keydown", function (event) {
var modifiers = event.altKey && event.ctrlKey && event.metaKey && var modifiers = event.altKey || event.ctrlKey || event.metaKey ||
event.shiftKey; event.shiftKey;
var mapped = map[event.which]; var mapped = map[event.which];
   
if (!modifiers) { if (!modifiers) {
if (mapped !== undefined) { if (mapped !== undefined) {
event.preventDefault(); event.preventDefault();
self.emit("move", mapped); self.emit("move", mapped);
} }
   
if (event.which === 32) self.restart.bind(self)(event); if (event.which === 32) self.restart.bind(self)(event);
} }
}); });
   
var retry = document.getElementsByClassName("retry-button")[0]; var retry = document.getElementsByClassName("retry-button")[0];
retry.addEventListener("click", this.restart.bind(this)); retry.addEventListener("click", this.restart.bind(this));
   
  // Listen to swipe events
  var touchStartClientX, touchStartClientY;
  var gameContainer = document.getElementsByClassName("game-container")[0];
  gameContainer.addEventListener("touchstart", function(event) {
  if (event.touches.length > 1) return;
   
  touchStartClientX = event.touches[0].clientX;
  touchStartClientY = event.touches[0].clientY;
  event.preventDefault();
  });
  gameContainer.addEventListener("touchmove", function(event) {
  event.preventDefault();
  });
  gameContainer.addEventListener("touchend", function(event) {
  if (event.touches.length > 0) {
  return;
  }
  var dx = event.changedTouches[0].clientX - touchStartClientX;
  var absDx = Math.abs(dx);
   
  var dy = event.changedTouches[0].clientY - touchStartClientY;
  var absDy = Math.abs(dy);
   
  if (Math.max(absDx, absDy) > 10) {
  // (right : left) : (down : up)
  self.emit("move", absDx > absDy ? (dx > 0 ? 1 : 3) : (dy > 0 ? 2 : 0));
  }
  });
}; };
   
KeyboardInputManager.prototype.restart = function (event) { KeyboardInputManager.prototype.restart = function (event) {
event.preventDefault(); event.preventDefault();
this.emit("restart"); this.emit("restart");
}; };
   
 Binary files /dev/null and b/meta/apple-touch-icon.png differ
// Exponent // Exponent
// From: https://github.com/Team-Sass/Sassy-math/blob/master/sass/math.scss#L36 // From: https://github.com/Team-Sass/Sassy-math/blob/master/sass/math.scss#L36
   
@function exponent($base, $exponent) { @function exponent($base, $exponent) {
// reset value // reset value
$value: $base; $value: $base;
// positive intergers get multiplied // positive intergers get multiplied
@if $exponent > 1 { @if $exponent > 1 {
@for $i from 2 through $exponent { @for $i from 2 through $exponent {
$value: $value * $base; } } $value: $value * $base; } }
// negitive intergers get divided. A number divided by itself is 1 // negitive intergers get divided. A number divided by itself is 1
@if $exponent < 1 { @if $exponent < 1 {
@for $i from 0 through -$exponent { @for $i from 0 through -$exponent {
$value: $value / $base; } } $value: $value / $base; } }
// return the last value written // return the last value written
@return $value; @return $value;
} }
   
@function pow($base, $exponent) { @function pow($base, $exponent) {
@return exponent($base, $exponent); @return exponent($base, $exponent);
} }
   
// Transition mixins // Transition mixins
@mixin transition($args...) { @mixin transition($args...) {
-webkit-transition: $args; -webkit-transition: $args;
-moz-transition: $args; -moz-transition: $args;
} }
   
@mixin transition-property($args...) { @mixin transition-property($args...) {
-webkit-transition-property: $args; -webkit-transition-property: $args;
-moz-transition-property: $args; -moz-transition-property: $args;
} }
   
// Keyframe animations // Keyframe animations
@mixin keyframes($animation-name) { @mixin keyframes($animation-name) {
@-webkit-keyframes $animation-name { @-webkit-keyframes $animation-name {
@content; @content;
} }
@-moz-keyframes $animation-name { @-moz-keyframes $animation-name {
@content; @content;
} }
@keyframes $animation-name { @keyframes $animation-name {
@content; @content;
} }
} }
   
@mixin animation($str) { @mixin animation($str) {
-webkit-animation: #{$str}; -webkit-animation: #{$str};
-moz-animation: #{$str}; -moz-animation: #{$str};
} }
   
@mixin animation-fill-mode($str) { @mixin animation-fill-mode($str) {
-webkit-animation-fill-mode: #{$str}; -webkit-animation-fill-mode: #{$str};
-moz-animation-fill-mode: #{$str}; -moz-animation-fill-mode: #{$str};
} }
   
  // Media queries
  @mixin smaller($width) {
  @media screen and (max-width: $width) {
  @content;
  }
  }
   
@import url(fonts/clear-sans.css); @import url(fonts/clear-sans.css);
html, body { html, body {
margin: 0; margin: 0;
padding: 0; padding: 0;
background: #faf8ef; background: #faf8ef;
color: #776e65; color: #776e65;
font-family: "Clear Sans", "Helvetica Neue", Arial, sans-serif; font-family: "Clear Sans", "Helvetica Neue", Arial, sans-serif;
font-size: 18px; } font-size: 18px; }
   
body { body {
margin: 80px 0; } margin: 80px 0; }
   
.heading:after { .heading:after {
content: ""; content: "";
display: block; display: block;
clear: both; } clear: both; }
   
h1.title { h1.title {
font-size: 80px; font-size: 80px;
font-weight: bold; font-weight: bold;
margin: 0; margin: 0;
display: block; display: block;
float: left; } float: left; }
   
@-webkit-keyframes move-up { @-webkit-keyframes move-up {
0% { 0% {
top: 25px; top: 25px;
opacity: 1; } opacity: 1; }
   
100% { 100% {
top: -50px; top: -50px;
opacity: 0; } } opacity: 0; } }
   
@-moz-keyframes move-up { @-moz-keyframes move-up {
0% { 0% {
top: 25px; top: 25px;
opacity: 1; } opacity: 1; }
   
100% { 100% {
top: -50px; top: -50px;
opacity: 0; } } opacity: 0; } }
   
@keyframes move-up { @keyframes move-up {
0% { 0% {
top: 25px; top: 25px;
opacity: 1; } opacity: 1; }
   
100% { 100% {
top: -50px; top: -50px;
opacity: 0; } } opacity: 0; } }
   
.score-container { .score-container {
position: relative; position: relative;
float: right; float: right;
background: #bbada0; background: #bbada0;
padding: 15px 25px; padding: 15px 25px;
font-size: 25px; font-size: 25px;
height: 25px; height: 25px;
line-height: 47px; line-height: 47px;
font-weight: bold; font-weight: bold;
border-radius: 3px; border-radius: 3px;
color: white; color: white;
margin-top: 8px; } margin-top: 8px; }
.score-container:after { .score-container:after {
position: absolute; position: absolute;
width: 100%; width: 100%;
top: 10px; top: 10px;
left: 0; left: 0;
content: "Score"; content: "Score";
text-transform: uppercase; text-transform: uppercase;
font-size: 13px; font-size: 13px;
line-height: 13px; line-height: 13px;
text-align: center; text-align: center;
color: #eee4da; } color: #eee4da; }
.score-container .score-addition { .score-container .score-addition {
position: absolute; position: absolute;
right: 30px; right: 30px;
color: red; color: red;
font-size: 25px; font-size: 25px;
line-height: 25px; line-height: 25px;
font-weight: bold; font-weight: bold;
color: rgba(119, 110, 101, 0.9); color: rgba(119, 110, 101, 0.9);
z-index: 100; z-index: 100;
-webkit-animation: move-up 600ms ease-in; -webkit-animation: move-up 600ms ease-in;
-moz-animation: move-up 600ms ease-in; -moz-animation: move-up 600ms ease-in;
-webkit-animation-fill-mode: both; -webkit-animation-fill-mode: both;
-moz-animation-fill-mode: both; } -moz-animation-fill-mode: both; }
   
p { p {
margin-top: 0; margin-top: 0;
margin-bottom: 10px; margin-bottom: 10px;
line-height: 1.65; } line-height: 1.65; }
   
a { a {
color: #776e65; color: #776e65;
font-weight: bold; font-weight: bold;
text-decoration: underline; text-decoration: underline;
cursor: pointer; } cursor: pointer; }
   
strong.important { strong.important {
text-transform: uppercase; } text-transform: uppercase; }
   
hr { hr {
border: none; border: none;
border-bottom: 1px solid #d8d4d0; border-bottom: 1px solid #d8d4d0;
margin-top: 20px; margin-top: 20px;
margin-bottom: 30px; } margin-bottom: 30px; }
   
.container { .container {
width: 500px; width: 500px;
margin: 0 auto; } margin: 0 auto; }
   
@-webkit-keyframes fade-in { @-webkit-keyframes fade-in {
0% { 0% {
opacity: 0; } opacity: 0; }
   
100% { 100% {
opacity: 1; } } opacity: 1; } }
   
@-moz-keyframes fade-in { @-moz-keyframes fade-in {
0% { 0% {
opacity: 0; } opacity: 0; }
   
100% { 100% {
opacity: 1; } } opacity: 1; } }
   
@keyframes fade-in { @keyframes fade-in {
0% { 0% {
opacity: 0; } opacity: 0; }
   
100% { 100% {
opacity: 1; } } opacity: 1; } }
   
.game-container { .game-container {
margin-top: 40px; margin-top: 40px;
position: relative; position: relative;
padding: 15px; padding: 15px;
cursor: default; cursor: default;
-webkit-touch-callout: none; -webkit-touch-callout: none;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
background: #bbada0; background: #bbada0;
border-radius: 6px; border-radius: 6px;
width: 500px; width: 500px;
height: 500px; height: 500px;
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
box-sizing: border-box; } box-sizing: border-box; }
.game-container .game-message { .game-container .game-message {
display: none; display: none;
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
background: rgba(238, 228, 218, 0.5); background: rgba(238, 228, 218, 0.5);
z-index: 100; z-index: 100;
text-align: center; text-align: center;
-webkit-animation: fade-in 800ms ease 1200ms; -webkit-animation: fade-in 800ms ease 1200ms;
-moz-animation: fade-in 800ms ease 1200ms; -moz-animation: fade-in 800ms ease 1200ms;
-webkit-animation-fill-mode: both; -webkit-animation-fill-mode: both;
-moz-animation-fill-mode: both; } -moz-animation-fill-mode: both; }
.game-container .game-message p { .game-container .game-message p {
font-size: 60px; font-size: 60px;
font-weight: bold; font-weight: bold;
height: 60px; height: 60px;
line-height: 60px; line-height: 60px;
margin-top: 222px; } margin-top: 222px; }
.game-container .game-message .lower { .game-container .game-message .lower {
display: block; display: block;
margin-top: 59px; } margin-top: 59px; }
.game-container .game-message a { .game-container .game-message a {
display: inline-block; display: inline-block;
background: #8f7a66; background: #8f7a66;
border-radius: 3px; border-radius: 3px;
padding: 0 20px; padding: 0 20px;
text-decoration: none; text-decoration: none;
color: #f9f6f2; color: #f9f6f2;
height: 40px; height: 40px;
line-height: 42px; line-height: 42px;
margin-left: 9px; } margin-left: 9px; }
.game-container .game-message.game-won { .game-container .game-message.game-won {
background: rgba(237, 194, 46, 0.5); background: rgba(237, 194, 46, 0.5);
color: #f9f6f2; } color: #f9f6f2; }
.game-container .game-message.game-won, .game-container .game-message.game-over { .game-container .game-message.game-won, .game-container .game-message.game-over {
display: block; } display: block; }
   
.grid-container { .grid-container {
position: absolute; position: absolute;
z-index: 1; } z-index: 1; }
   
.grid-row { .grid-row {
margin-bottom: 15px; } margin-bottom: 15px; }
.grid-row:last-child { .grid-row:last-child {
margin-bottom: 0; } margin-bottom: 0; }
.grid-row:after { .grid-row:after {
content: ""; content: "";
display: block; display: block;
clear: both; } clear: both; }
   
.grid-cell { .grid-cell {
width: 106.25px; width: 106.25px;
height: 106.25px; height: 106.25px;
margin-right: 15px; margin-right: 15px;
float: left; float: left;
border-radius: 3px; border-radius: 3px;
background: rgba(238, 228, 218, 0.35); } background: rgba(238, 228, 218, 0.35); }
.grid-cell:last-child { .grid-cell:last-child {
margin-right: 0; } margin-right: 0; }
   
.tile-container { .tile-container {
position: absolute; position: absolute;
z-index: 2; } z-index: 2; }
   
.tile { .tile {
background: red;  
width: 106.25px; width: 106.25px;
height: 106.25px; height: 106.25px;
  line-height: 116.25px; }
  .tile.tile-position-1-1 {
  position: absolute;
  left: 0px;
  top: 0px; }
  .tile.tile-position-1-2 {
  position: absolute;
  left: 0px;
  top: 121px; }
  .tile.tile-position-1-3 {
  position: absolute;
  left: 0px;
  top: 243px; }
  .tile.tile-position-1-4 {
  position: absolute;
  left: 0px;
  top: 364px; }
  .tile.tile-position-2-1 {
  position: absolute;
  left: 121px;
  top: 0px; }
  .tile.tile-position-2-2 {
  position: absolute;
  left: 121px;
  top: 121px; }
  .tile.tile-position-2-3 {
  position: absolute;
  left: 121px;
  top: 243px; }
  .tile.tile-position-2-4 {
  position: absolute;
  left: 121px;
  top: 364px; }
  .tile.tile-position-3-1 {
  position: absolute;
  left: 243px;
  top: 0px; }
  .tile.tile-position-3-2 {
  position: absolute;
  left: 243px;
  top: 121px; }
  .tile.tile-position-3-3 {
  position: absolute;
  left: 243px;
  top: 243px; }
  .tile.tile-position-3-4 {
  position: absolute;
  left: 243px;
  top: 364px; }
  .tile.tile-position-4-1 {
  position: absolute;
  left: 364px;
  top: 0px; }
  .tile.tile-position-4-2 {
  position: absolute;
  left: 364px;
  top: 121px; }
  .tile.tile-position-4-3 {
  position: absolute;
  left: 364px;
  top: 243px; }
  .tile.tile-position-4-4 {
  position: absolute;
  left: 364px;
  top: 364px; }
   
  .tile {
border-radius: 3px; border-radius: 3px;
background: #eee4da; background: #eee4da;
text-align: center; text-align: center;
line-height: 116.25px;  
font-size: 55px;  
font-weight: bold; font-weight: bold;
z-index: 10; z-index: 10;
  font-size: 55px;
-webkit-transition: 100ms ease-in-out; -webkit-transition: 100ms ease-in-out;
-moz-transition: 100ms ease-in-out; -moz-transition: 100ms ease-in-out;
-webkit-transition-property: top, left; -webkit-transition-property: top, left;
-moz-transition-property: top, left; } -moz-transition-property: top, left; }
.tile.tile-position-1-1 {  
position: absolute;  
left: 0px;  
top: 0px; }  
.tile.tile-position-1-2 {  
position: absolute;  
left: 0px;  
top: 121px; }  
.tile.tile-position-1-3 {  
position: absolute;  
left: 0px;  
top: 243px; }  
.tile.tile-position-1-4 {  
position: absolute;  
left: 0px;  
top: 364px; }  
.tile.tile-position-2-1 {  
position: absolute;  
left: 121px;  
top: 0px; }  
.tile.tile-position-2-2 {  
position: absolute;  
left: 121px;  
top: 121px; }  
.tile.tile-position-2-3 {  
position: absolute;  
left: 121px;  
top: 243px; }  
.tile.tile-position-2-4 {  
position: absolute;  
left: 121px;  
top: 364px; }  
.tile.tile-position-3-1 {  
position: absolute;  
left: 243px;  
top: 0px; }  
.tile.tile-position-3-2 {  
position: absolute;  
left: 243px;  
top: 121px; }  
.tile.tile-position-3-3 {  
position: absolute;  
left: 243px;  
top: 243px; }  
.tile.tile-position-3-4 {  
position: absolute;  
left: 243px;  
top: 364px; }  
.tile.tile-position-4-1 {  
position: absolute;  
left: 364px;  
top: 0px; }  
.tile.tile-position-4-2 {  
position: absolute;  
left: 364px;  
top: 121px; }  
.tile.tile-position-4-3 {  
position: absolute;  
left: 364px;  
top: 243px; }  
.tile.tile-position-4-4 {  
position: absolute;  
left: 364px;  
top: 364px; }  
.tile.tile-2 { .tile.tile-2 {
background: #eee4da; background: #eee4da;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0); } box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0); }
.tile.tile-4 { .tile.tile-4 {
background: #ede0c8; background: #ede0c8;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0); } box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0); }
.tile.tile-8 { .tile.tile-8 {
color: #f9f6f2; color: #f9f6f2;
background: #f2b179; } background: #f2b179; }
.tile.tile-16 { .tile.tile-16 {
color: #f9f6f2; color: #f9f6f2;
background: #f59563; } background: #f59563; }
.tile.tile-32 { .tile.tile-32 {
color: #f9f6f2; color: #f9f6f2;
background: #f67c5f; } background: #f67c5f; }
.tile.tile-64 { .tile.tile-64 {
color: #f9f6f2; color: #f9f6f2;
background: #f65e3b; } background: #f65e3b; }
.tile.tile-128 { .tile.tile-128 {
color: #f9f6f2; color: #f9f6f2;
background: #edcf72; background: #edcf72;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.2381), inset 0 0 0 1px rgba(255, 255, 255, 0.14286); box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.2381), inset 0 0 0 1px rgba(255, 255, 255, 0.14286);
font-size: 45px; } font-size: 45px; }
  @media screen and (max-width: 480px) {
  .tile.tile-128 {
  font-size: 25px; } }
.tile.tile-256 { .tile.tile-256 {
color: #f9f6f2; color: #f9f6f2;
background: #edcc61; background: #edcc61;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.31746), inset 0 0 0 1px rgba(255, 255, 255, 0.19048); box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.31746), inset 0 0 0 1px rgba(255, 255, 255, 0.19048);
font-size: 45px; } font-size: 45px; }
  @media screen and (max-width: 480px) {
  .tile.tile-256 {
  font-size: 25px; } }
.tile.tile-512 { .tile.tile-512 {
color: #f9f6f2; color: #f9f6f2;
background: #edc850; background: #edc850;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.39683), inset 0 0 0 1px rgba(255, 255, 255, 0.2381); box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.39683), inset 0 0 0 1px rgba(255, 255, 255, 0.2381);
font-size: 45px; } font-size: 45px; }
  @media screen and (max-width: 480px) {
  .tile.tile-512 {
  font-size: 25px; } }
.tile.tile-1024 { .tile.tile-1024 {
color: #f9f6f2; color: #f9f6f2;
background: #edc53f; background: #edc53f;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.47619), inset 0 0 0 1px rgba(255, 255, 255, 0.28571); box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.47619), inset 0 0 0 1px rgba(255, 255, 255, 0.28571);
font-size: 35px; } font-size: 35px; }
  @media screen and (max-width: 480px) {
  .tile.tile-1024 {
  font-size: 15px; } }
.tile.tile-2048 { .tile.tile-2048 {
color: #f9f6f2; color: #f9f6f2;
background: #edc22e; background: #edc22e;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.55556), inset 0 0 0 1px rgba(255, 255, 255, 0.33333); box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.55556), inset 0 0 0 1px rgba(255, 255, 255, 0.33333);
font-size: 35px; } font-size: 35px; }
  @media screen and (max-width: 480px) {
  .tile.tile-2048 {
  font-size: 15px; } }
   
@-webkit-keyframes appear { @-webkit-keyframes appear {
0% { 0% {
opacity: 0; opacity: 0;
-webkit-transform: scale(0); -webkit-transform: scale(0);
-moz-transform: scale(0); } -moz-transform: scale(0); }
   
100% { 100% {
opacity: 1; opacity: 1;
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); } } -moz-transform: scale(1); } }
   
@-moz-keyframes appear { @-moz-keyframes appear {
0% { 0% {
opacity: 0; opacity: 0;
-webkit-transform: scale(0); -webkit-transform: scale(0);
-moz-transform: scale(0); } -moz-transform: scale(0); }
   
100% { 100% {
opacity: 1; opacity: 1;
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); } } -moz-transform: scale(1); } }
   
@keyframes appear { @keyframes appear {
0% { 0% {
opacity: 0; opacity: 0;
-webkit-transform: scale(0); -webkit-transform: scale(0);
-moz-transform: scale(0); } -moz-transform: scale(0); }
   
100% { 100% {
opacity: 1; opacity: 1;
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); } } -moz-transform: scale(1); } }
   
.tile-new { .tile-new {
-webkit-animation: appear 200ms ease 100ms; -webkit-animation: appear 200ms ease 100ms;
-moz-animation: appear 200ms ease 100ms; -moz-animation: appear 200ms ease 100ms;
-webkit-animation-fill-mode: both; -webkit-animation-fill-mode: both;
-moz-animation-fill-mode: both; } -moz-animation-fill-mode: both; }
   
@-webkit-keyframes pop { @-webkit-keyframes pop {
0% { 0% {
-webkit-transform: scale(0); -webkit-transform: scale(0);
-moz-transform: scale(0); } -moz-transform: scale(0); }
   
50% { 50% {
-webkit-transform: scale(1.2); -webkit-transform: scale(1.2);
-moz-transform: scale(1.2); } -moz-transform: scale(1.2); }
   
100% { 100% {
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); } } -moz-transform: scale(1); } }
   
@-moz-keyframes pop { @-moz-keyframes pop {
0% { 0% {
-webkit-transform: scale(0); -webkit-transform: scale(0);
-moz-transform: scale(0); } -moz-transform: scale(0); }
   
50% { 50% {
-webkit-transform: scale(1.2); -webkit-transform: scale(1.2);
-moz-transform: scale(1.2); } -moz-transform: scale(1.2); }
   
100% { 100% {
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); } } -moz-transform: scale(1); } }
   
@keyframes pop { @keyframes pop {
0% { 0% {
-webkit-transform: scale(0); -webkit-transform: scale(0);
-moz-transform: scale(0); } -moz-transform: scale(0); }
   
50% { 50% {
-webkit-transform: scale(1.2); -webkit-transform: scale(1.2);
-moz-transform: scale(1.2); } -moz-transform: scale(1.2); }
   
100% { 100% {
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); } } -moz-transform: scale(1); } }
   
.tile-merged { .tile-merged {
z-index: 20; z-index: 20;
-webkit-animation: pop 200ms ease 100ms; -webkit-animation: pop 200ms ease 100ms;
-moz-animation: pop 200ms ease 100ms; -moz-animation: pop 200ms ease 100ms;
-webkit-animation-fill-mode: both; -webkit-animation-fill-mode: both;
-moz-animation-fill-mode: both; } -moz-animation-fill-mode: both; }
   
.game-intro { .game-intro {
margin-bottom: 0; } margin-bottom: 0; }
   
.game-explanation { .game-explanation {
margin-top: 50px; } margin-top: 50px; }
   
  @media screen and (max-width: 480px) {
  html, body {
  font-size: 15px; }
   
  body {
  margin: 20px 0;
  padding: 0 20px; }
   
  h1.title {
  font-size: 50px; }
   
  .container {
  width: 280px;
  margin: 0 auto; }
   
  .score-container {
  margin-top: 0; }
   
  .heading {
  margin-bottom: 10px; }
   
  .game-container {
  margin-top: 40px;
  position: relative;
  padding: 10px;
  cursor: default;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  background: #bbada0;
  border-radius: 6px;
  width: 280px;
  height: 280px;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box; }
  .game-container .game-message {
  display: none;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba(238, 228, 218, 0.5);
  z-index: 100;
  text-align: center;
  -webkit-animation: fade-in 800ms ease 1200ms;
  -moz-animation: fade-in 800ms ease 1200ms;
  -webkit-animation-fill-mode: both;
  -moz-animation-fill-mode: both; }
  .game-container .game-message p {
  font-size: 60px;
  font-weight: bold;
  height: 60px;
  line-height: 60px;
  margin-top: 222px; }
  .game-container .game-message .lower {
  display: block;
  margin-top: 59px; }
  .game-container .game-message a {
  display: inline-block;
  background: #8f7a66;
  border-radius: 3px;
  padding: 0 20px;
  text-decoration: none;
  color: #f9f6f2;
  height: 40px;
  line-height: 42px;
  margin-left: 9px; }
  .game-container .game-message.game-won {
  background: rgba(237, 194, 46, 0.5);
  color: #f9f6f2; }
  .game-container .game-message.game-won, .game-container .game-message.game-over {
  display: block; }
   
  .grid-container {
  position: absolute;
  z-index: 1; }
   
  .grid-row {
  margin-bottom: 10px; }
  .grid-row:last-child {
  margin-bottom: 0; }
  .grid-row:after {
  content: "";
  display: block;
  clear: both; }
   
  .grid-cell {
  width: 57.5px;
  height: 57.5px;
  margin-right: 10px;
  float: left;
  border-radius: 3px;
  background: rgba(238, 228, 218, 0.35); }
  .grid-cell:last-child {
  margin-right: 0; }
   
  .tile-container {
  position: absolute;
  z-index: 2; }
   
  .tile {
  width: 57.5px;
  height: 57.5px;
  line-height: 67.5px; }
  .tile.tile-position-1-1 {
  position: absolute;
  left: 0px;
  top: 0px; }
  .tile.tile-position-1-2 {
  position: absolute;
  left: 0px;
  top: 68px; }
  .tile.tile-position-1-3 {
  position: absolute;
  left: 0px;
  top: 135px; }
  .tile.tile-position-1-4 {
  position: absolute;
  left: 0px;
  top: 203px; }
  .tile.tile-position-2-1 {
  position: absolute;
  left: 68px;
  top: 0px; }
  .tile.tile-position-2-2 {
  position: absolute;
  left: 68px;
  top: 68px; }
  .tile.tile-position-2-3 {
  position: absolute;
  left: 68px;
  top: 135px; }
  .tile.tile-position-2-4 {
  position: absolute;
  left: 68px;
  top: 203px; }
  .tile.tile-position-3-1 {
  position: absolute;
  left: 135px;
  top: 0px; }
  .tile.tile-position-3-2 {
  position: absolute;
  left: 135px;
  top: 68px; }
  .tile.tile-position-3-3 {
  position: absolute;
  left: 135px;
  top: 135px; }
  .tile.tile-position-3-4 {
  position: absolute;
  left: 135px;
  top: 203px; }
  .tile.tile-position-4-1 {
  position: absolute;
  left: 203px;
  top: 0px; }
  .tile.tile-position-4-2 {
  position: absolute;
  left: 203px;
  top: 68px; }
  .tile.tile-position-4-3 {
  position: absolute;
  left: 203px;
  top: 135px; }
  .tile.tile-position-4-4 {
  position: absolute;
  left: 203px;
  top: 203px; }
   
  .game-container {
  margin-top: 20px; }
   
  .tile {
  font-size: 35px; }
   
  .game-message p {
  font-size: 30px !important;
  height: 30px !important;
  line-height: 30px !important;
  margin-top: 90px !important; }
  .game-message .lower {
  margin-top: 30px !important; } }
   
@import "helpers"; @import "helpers";
@import "fonts/clear-sans.css"; @import "fonts/clear-sans.css";
   
$field-width: 500px; $field-width: 500px;
$grid-spacing: 15px; $grid-spacing: 15px;
$grid-row-cells: 4; $grid-row-cells: 4;
$tile-size: ($field-width - $grid-spacing * ($grid-row-cells + 1)) / $grid-row-cells; $tile-size: ($field-width - $grid-spacing * ($grid-row-cells + 1)) / $grid-row-cells;
$tile-border-radius: 3px; $tile-border-radius: 3px;
   
$text-color: #776E65; $text-color: #776E65;
$bright-text-color: #f9f6f2; $bright-text-color: #f9f6f2;
   
$tile-color: #eee4da; $tile-color: #eee4da;
$tile-gold-color: #edc22e; $tile-gold-color: #edc22e;
$tile-gold-glow-color: lighten($tile-gold-color, 15%); $tile-gold-glow-color: lighten($tile-gold-color, 15%);
   
$game-container-background: #bbada0; $game-container-background: #bbada0;
   
$transition-speed: 100ms; $transition-speed: 100ms;
   
html, body { html, body {
margin: 0; margin: 0;
padding: 0; padding: 0;
   
background: #faf8ef; background: #faf8ef;
color: $text-color; color: $text-color;
font-family: "Clear Sans", "Helvetica Neue", Arial, sans-serif; font-family: "Clear Sans", "Helvetica Neue", Arial, sans-serif;
font-size: 18px; font-size: 18px;
} }
   
body { body {
margin: 80px 0; margin: 80px 0;
} }
   
.heading:after { .heading:after {
content: ""; content: "";
display: block; display: block;
clear: both; clear: both;
} }
   
h1.title { h1.title {
font-size: 80px; font-size: 80px;
font-weight: bold; font-weight: bold;
margin: 0; margin: 0;
display: block; display: block;
float: left; float: left;
} }
   
@include keyframes(move-up) { @include keyframes(move-up) {
0% { 0% {
top: 25px; top: 25px;
opacity: 1; opacity: 1;
} }
   
100% { 100% {
top: -50px; top: -50px;
opacity: 0; opacity: 0;
} }
} }
   
.score-container { .score-container {
$height: 25px; $height: 25px;
   
position: relative; position: relative;
float: right; float: right;
background: $game-container-background; background: $game-container-background;
padding: 15px 25px; padding: 15px 25px;
font-size: $height; font-size: $height;
height: $height; height: $height;
line-height: $height + 22px; line-height: $height + 22px;
font-weight: bold; font-weight: bold;
border-radius: 3px; border-radius: 3px;
color: white; color: white;
margin-top: 8px; margin-top: 8px;
   
&:after { &:after {
position: absolute; position: absolute;
width: 100%; width: 100%;
top: 10px; top: 10px;
left: 0; left: 0;
content: "Score"; content: "Score";
text-transform: uppercase; text-transform: uppercase;
font-size: 13px; font-size: 13px;
line-height: 13px; line-height: 13px;
text-align: center; text-align: center;
color: $tile-color; color: $tile-color;
} }
   
.score-addition { .score-addition {
position: absolute; position: absolute;
right: 30px; right: 30px;
color: red; color: red;
font-size: $height; font-size: $height;
line-height: $height; line-height: $height;
font-weight: bold; font-weight: bold;
color: rgba($text-color, .9); color: rgba($text-color, .9);
z-index: 100; z-index: 100;
@include animation(move-up 600ms ease-in); @include animation(move-up 600ms ease-in);
@include animation-fill-mode(both); @include animation-fill-mode(both);
} }
} }
   
p { p {
margin-top: 0; margin-top: 0;
margin-bottom: 10px; margin-bottom: 10px;
line-height: 1.65; line-height: 1.65;
} }
   
a { a {
color: $text-color; color: $text-color;
font-weight: bold; font-weight: bold;
text-decoration: underline; text-decoration: underline;
cursor: pointer; cursor: pointer;
} }
   
strong { strong {
&.important { &.important {
text-transform: uppercase; text-transform: uppercase;
} }
} }
   
hr { hr {
border: none; border: none;
border-bottom: 1px solid lighten($text-color, 40%); border-bottom: 1px solid lighten($text-color, 40%);
margin-top: 20px; margin-top: 20px;
margin-bottom: 30px; margin-bottom: 30px;
} }
   
.container { .container {
width: $field-width; width: $field-width;
margin: 0 auto; margin: 0 auto;
} }
   
@include keyframes(fade-in) { @include keyframes(fade-in) {
0% { 0% {
opacity: 0; opacity: 0;
} }
   
100% { 100% {
opacity: 1; opacity: 1;
} }
} }
   
.game-container { // Styles for buttons
margin-top: 40px; @mixin button {
position: relative; display: inline-block;
padding: $grid-spacing; background: darken($game-container-background, 20%);
  border-radius: 3px;
cursor: default; padding: 0 20px;
-webkit-touch-callout: none; text-decoration: none;
-webkit-user-select: none; color: $bright-text-color;
-moz-user-select: none; height: 40px;
  line-height: 42px;
background: $game-container-background; }
border-radius: $tile-border-radius * 2;  
width: $field-width; // Game field mixin used to render CSS at different width
height: $field-width; @mixin game-field {
-webkit-box-sizing: border-box; .game-container {
-moz-box-sizing: border-box; margin-top: 40px;
box-sizing: border-box; position: relative;
  padding: $grid-spacing;
.game-message {  
display: none; cursor: default;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -moz-user-select: none;
   
  background: $game-container-background;
  border-radius: $tile-border-radius * 2;
  width: $field-width;
  height: $field-width;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
   
  .game-message {
  display: none;
   
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba($tile-color, .5);
  z-index: 100;
   
  text-align: center;
   
  p {
  font-size: 60px;
  font-weight: bold;
  height: 60px;
  line-height: 60px;
  margin-top: 222px;
  // height: $field-width;
  // line-height: $field-width;
  }
   
  .lower {
  display: block;
  margin-top: 59px;
  }
   
  a {
  @include button;
  margin-left: 9px;
  // margin-top: 59px;
  }
   
  @include animation(fade-in 800ms ease $transition-speed * 12);
  @include animation-fill-mode(both);
   
  &.game-won {
  background: rgba($tile-gold-color, .5);
  color: $bright-text-color;
  }
   
  &.game-won, &.game-over {
  display: block;
  }
  }
  }
   
  .grid-container {
position: absolute; position: absolute;
top: 0; z-index: 1;
right: 0; }
bottom: 0;  
left: 0; .grid-row {
background: rgba($tile-color, .5); margin-bottom: $grid-spacing;
z-index: 100;  
  &:last-child {
text-align: center; margin-bottom: 0;
  }
p {  
font-size: 60px; &:after {
font-weight: bold; content: "";
height: 60px;  
line-height: 60px;  
margin-top: 222px;  
// height: $field-width;  
// line-height: $field-width;  
}  
   
.lower {  
display: block; display: block;
margin-top: 59px; clear: both;
} }
  }
a {  
display: inline-block; .grid-cell {
background: darken($game-container-background, 20%); width: $tile-size;
border-radius: 3px; height: $tile-size;
padding: 0 20px; margin-right: $grid-spacing;
text-decoration: none; float: left;
color: $bright-text-color;  
height: 40px; border-radius: $tile-border-radius;
line-height: 42px;  
margin-left: 9px; background: rgba($tile-color, .35);
// margin-top: 59px;  
} &:last-child {
  margin-right: 0;
@include animation(fade-in 800ms ease $transition-speed * 12); }
@include animation-fill-mode(both); }
   
&.game-won { .tile-container {
background: rgba($tile-gold-color, .5); position: absolute;
color: $bright-text-color; z-index: 2;
} }
   
&.game-won, &.game-over { .tile {
display: block; width: $tile-size;
} height: $tile-size;
} line-height: $tile-size + 10px;
}  
  // Build position classes
.grid-container { @for $x from 1 through $grid-row-cells {
position: absolute; @for $y from 1 through $grid-row-cells {
z-index: 1; &.tile-position-#{$x}-#{$y} {
} position: absolute;
  left: round(($tile-size + $grid-spacing) * ($x - 1));
.grid-row { top: round(($tile-size + $grid-spacing) * ($y - 1));
margin-bottom: $grid-spacing; }
  }
&:last-child { }
margin-bottom: 0; }
} }
   
&:after { // End of game-field mixin
content: ""; @include game-field;
display: block;  
clear: both;  
}  
}  
   
.grid-cell {  
width: $tile-size;  
height: $tile-size;  
margin-right: $grid-spacing;  
float: left;  
   
border-radius: $tile-border-radius;  
   
background: rgba($tile-color, .35);  
   
&:last-child {  
margin-right: 0;  
}  
}  
   
.tile-container {  
position: absolute;  
z-index: 2;  
}  
   
.tile { .tile {
background: red;  
width: $tile-size;  
height: $tile-size;  
border-radius: $tile-border-radius; border-radius: $tile-border-radius;
   
background: $tile-color; background: $tile-color;
text-align: center; text-align: center;
line-height: $tile-size + 10px;  
font-size: 55px;  
font-weight: bold; font-weight: bold;
z-index: 10; z-index: 10;
   
  font-size: 55px;
   
@include transition($transition-speed ease-in-out); @include transition($transition-speed ease-in-out);
@include transition-property(top, left); @include transition-property(top, left);
   
// Build position classes  
@for $x from 1 through $grid-row-cells {  
@for $y from 1 through $grid-row-cells {  
&.tile-position-#{$x}-#{$y} {  
position: absolute;  
left: round(($tile-size + $grid-spacing) * ($x - 1));  
top: round(($tile-size + $grid-spacing) * ($y - 1));  
}  
}  
}  
   
$base: 2; $base: 2;
$exponent: 1; $exponent: 1;
$limit: 11; $limit: 11;
   
// Colors for all 11 states, false = no special color // Colors for all 11 states, false = no special color
$special-colors: false false, // 2 $special-colors: false false, // 2
false false, // 4 false false, // 4
#f78e48 true, // 8 #f78e48 true, // 8
#fc5e2e true, // 16 #fc5e2e true, // 16
#ff3333 true, // 32 #ff3333 true, // 32
#ff0000 true, // 64 #ff0000 true, // 64
false true, // 128 false true, // 128
false true, // 256 false true, // 256
false true, // 512 false true, // 512
false true, // 1024 false true, // 1024
false true; // 2048 false true; // 2048
   
// Build tile colors // Build tile colors
@while $exponent <= $limit { @while $exponent <= $limit {
$power: pow($base, $exponent); $power: pow($base, $exponent);
   
&.tile-#{$power} { &.tile-#{$power} {
// Calculate base background color // Calculate base background color
$gold-percent: ($exponent - 1) / ($limit - 1) * 100; $gold-percent: ($exponent - 1) / ($limit - 1) * 100;
$mixed-background: mix($tile-gold-color, $tile-color, $gold-percent); $mixed-background: mix($tile-gold-color, $tile-color, $gold-percent);
   
$nth-color: nth($special-colors, $exponent); $nth-color: nth($special-colors, $exponent);
   
$special-background: nth($nth-color, 1); $special-background: nth($nth-color, 1);
$bright-color: nth($nth-color, 2); $bright-color: nth($nth-color, 2);
   
@if $special-background { @if $special-background {
$mixed-background: mix($special-background, $mixed-background, 55%); $mixed-background: mix($special-background, $mixed-background, 55%);
} }
   
@if $bright-color { @if $bright-color {
color: $bright-text-color; color: $bright-text-color;
} }
   
// Set background // Set background
background: $mixed-background; background: $mixed-background;
   
// Add glow // Add glow
$glow-opacity: max($exponent - 4, 0) / ($limit - 4); $glow-opacity: max($exponent - 4, 0) / ($limit - 4);
   
@if not $special-background { @if not $special-background {
box-shadow: 0 0 30px 10px rgba($tile-gold-glow-color, $glow-opacity / 1.8), box-shadow: 0 0 30px 10px rgba($tile-gold-glow-color, $glow-opacity / 1.8),
inset 0 0 0 1px rgba(white, $glow-opacity / 3); inset 0 0 0 1px rgba(white, $glow-opacity / 3);
} }
   
// Adjust font size for bigger numbers // Adjust font size for bigger numbers
@if $power >= 100 and $power < 1000 { @if $power >= 100 and $power < 1000 {
font-size: 45px; font-size: 45px;
   
  // Media queries placed here to avoid carrying over the rest of the logic
  @include smaller(480px) {
  font-size: 25px;
  }
} @else if $power >= 1000 { } @else if $power >= 1000 {
font-size: 35px; font-size: 35px;
   
  @include smaller(480px) {
  font-size: 15px;
  }
} }
} }
   
$exponent: $exponent + 1; $exponent: $exponent + 1;
} }
} }
   
@include keyframes(appear) { @include keyframes(appear) {
0% { 0% {
opacity: 0; opacity: 0;
-webkit-transform: scale(0); -webkit-transform: scale(0);
-moz-transform: scale(0); -moz-transform: scale(0);
} }
   
100% { 100% {
opacity: 1; opacity: 1;
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); -moz-transform: scale(1);
} }
} }
   
.tile-new { .tile-new {
@include animation(appear 200ms ease $transition-speed); @include animation(appear 200ms ease $transition-speed);
@include animation-fill-mode(both); @include animation-fill-mode(both);
} }
   
@include keyframes(pop) { @include keyframes(pop) {
0% { 0% {
-webkit-transform: scale(0); -webkit-transform: scale(0);
-moz-transform: scale(0); -moz-transform: scale(0);
} }
   
50% { 50% {
-webkit-transform: scale(1.2); -webkit-transform: scale(1.2);
-moz-transform: scale(1.2); -moz-transform: scale(1.2);
} }
   
100% { 100% {
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); -moz-transform: scale(1);
} }
} }
   
.tile-merged { .tile-merged {
z-index: 20; z-index: 20;
@include animation(pop 200ms ease $transition-speed); @include animation(pop 200ms ease $transition-speed);
@include animation-fill-mode(both); @include animation-fill-mode(both);
} }
   
.game-intro { .game-intro {
margin-bottom: 0; margin-bottom: 0;
} }
   
.game-explanation { .game-explanation {
margin-top: 50px; margin-top: 50px;
} }
   
  @include smaller(480px) {
  // Redefine variables for smaller screens
  $field-width: 280px;
  $grid-spacing: 10px;
  $grid-row-cells: 4;
  $tile-size: ($field-width - $grid-spacing * ($grid-row-cells + 1)) / $grid-row-cells;
  $tile-border-radius: 3px;
   
  html, body {
  font-size: 15px;
  }
   
  body {
  margin: 20px 0;
  padding: 0 20px;
  }
   
  h1.title {
  font-size: 50px;
  }
   
  .container {
  width: $field-width;
  margin: 0 auto;
  }
   
  .score-container {
  margin-top: 0;
  }
   
  .heading {
  margin-bottom: 10px;
  }
   
  // Render the game field at the right width
  @include game-field;
   
  .game-container {
  margin-top: 20px;
  }
   
  // Rest of the font-size adjustments in the tile class
  .tile {
  font-size: 35px;
  }
   
  .game-message {
  p {
  font-size: 30px !important;
  height: 30px !important;
  line-height: 30px !important;
  margin-top: 90px !important;
  }
   
  .lower {
  margin-top: 30px !important;
  }
  }
  }
   
comments