├── .eslintrc ├── .gitignore ├── LICENCE.md ├── README.md ├── app ├── css │ └── theme.css ├── favicon.ico ├── fonts │ ├── meteocons.eot │ ├── meteocons.svg │ ├── meteocons.ttf │ ├── meteocons.woff │ ├── meteocons.woff2 │ ├── roboto-light.eot │ ├── roboto-light.svg │ ├── roboto-light.ttf │ ├── roboto-light.woff │ └── roboto-light.woff2 ├── index.html ├── js │ ├── angular-animate.min.js │ ├── angular.min.js │ └── app.js └── scss │ ├── _animations.scss │ ├── _fonts.scss │ ├── _normalize.scss │ └── theme.scss └── package.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "angular" 4 | ], 5 | 6 | "env": { 7 | "browser": true, 8 | "node": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node junk 2 | node_modules 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Max Milton 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A simple experiment using AngularJS, the OpenWeatherMap API, and CSS3. 2 | 3 | # Overview 4 | 5 | Mostly just a project to allow me to experiment with AngularJS. View a live version at https://labs.wearegenki.com/experiments/simple-weather-app/ 6 | 7 | Source code available on [Github](https://github.com/MaxMilton/Simple-Weather-App). 8 | 9 | ## Interesting Points 10 | 11 | * Gets weather via the OpenWeatherMap API using JSONP requests. 12 | * Uses browser built in geolocation to get coordinates. 13 | * CSS3 animations on colour change and location panel appearing. 14 | * Shows weather from a random city on page load and button. 15 | * Able to run on https even though OpenWeatherMap is http only by using a proxy 16 | 17 | ## Development Instructions 18 | 19 | 1. Watch SCSS for changes: `npm run-script watch` 20 | 2. Start the local web server: `npm start` 21 | 3. Open a new browser tab and go to: http://localhost:8880/ 22 | 23 | ## Made Using 24 | 25 | * Weather Icons - http://www.alessioatzeni.com/meteocons/ 26 | * OpenWeatherMap API - http://openweathermap.org/api 27 | * Normalize.css - https://necolas.github.io/normalize.css/ 28 | -------------------------------------------------------------------------------- /app/css/theme.css: -------------------------------------------------------------------------------- 1 | html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@font-face{font-family:Roboto;src:url("../fonts/roboto-light.eot");src:url("../fonts/roboto-light.eot?#iefix") format("eot"),url("../fonts/roboto-light.woff2") format("woff2"),url("../fonts/roboto-light.woff") format("woff"),url("../fonts/roboto-light.ttf") format("truetype");font-weight:300;font-style:normal}@font-face{font-family:Meteocons;src:url("../fonts/meteocons.eot");src:url("../fonts/meteocons.eot?#iefix") format("eot"),url("../fonts/meteocons.woff2") format("woff2"),url("../fonts/meteocons.woff") format("woff"),url("../fonts/meteocons.ttf") format("truetype");font-weight:normal;font-style:normal;text-rendering:auto;-moz-osx-font-smoothing:grayscale;transform:translate(0, 0)}.pull-down{animation-name:pullDown;-webkit-animation-name:pullDown;animation-duration:0.7s;-webkit-animation-duration:0.7s;animation-timing-function:ease-out;-webkit-animation-timing-function:ease-out;transform-origin:50% 0%;-ms-transform-origin:50% 0%;-webkit-transform-origin:50% 0%}@keyframes pullDown{0%{transform:scaleY(0.1)}100%{transform:scaleY(1)}}@-webkit-keyframes pullDown{0%{-webkit-transform:scaleY(0.1)}100%{-webkit-transform:scaleY(1)}}[ng-cloak]{display:none !important}html{background-color:#E0E0E0;color:#212121;font:300 1.2em Roboto,"Roboto",sans-serif;margin-left:1em;margin-right:1em}header{margin-bottom:2em}h1{font-size:2.3em;font-weight:300}.lead{font-size:1.3em}button{background-color:transparent;font-family:Roboto,"Roboto",sans-serif;font-size:1.2em;font-weight:300;border:none;padding:0.4em;opacity:0.6}button:focus{outline:none}.btn-random{box-shadow:1px 1px 6px rgba(0,0,0,0.2);border-radius:5px}.btn-random:hover{box-shadow:1px 2px 8px rgba(0,0,0,0.4)}.btn-random>.icon{vertical-align:-2px}.btn-locate:hover{opacity:1}.card{transition:background-color 0.7s ease;color:#fff;padding:1em;border-radius:5px;box-shadow:0 0 15px rgba(0,0,0,0.1);max-width:30em;position:relative;z-index:2}.card.orange{background-color:#FF9800}.card.orange .location-panel{background-color:#F57C00}.card.orange+.card{color:#F57C00}.card.blue{background-color:#2196F3}.card.blue .location-panel{background-color:#1976D2}.card.blue+.card{color:#1976D2}.card.purple{background-color:#673AB7}.card.purple .location-panel{background-color:#512DA8}.card.purple+.card{color:#512DA8}.card.grey{background-color:#607D8B}.card.grey .location-panel{background-color:#455A64}.card.grey+.card{color:#455A64}.card.green{background-color:#4CAF50}.card.green .location-panel{background-color:#388E3C}.card.green+.card{color:#388E3C}.card+.card{background-color:#fff;padding-top:0.6em;margin-top:-0.5em;z-index:1}.card+.card>p{transition:color 0.7s ease}.top{padding:0.3em 1em 1em}.temp-now{font-size:6em;margin:0}.temp-now>span{font-size:0.6em;vertical-align:0.48em}.weather-now{font-size:2em;text-transform:capitalize;margin:0.1em 0 0.2em}.highlow{font-size:1.2em;margin:0 0 0.2em}.location-panel{transition:background-color 0.7s ease;font-size:1.1em;border-radius:5px}.search{display:inline}.location{background:url('data:image/svg+xml;utf8,') 0.4em 50% no-repeat;color:#fff;font-family:Roboto,"Roboto",sans-serif;font-size:1.4em;font-weight:300;background-color:transparent;border:none;padding:0.4em 0 0.4em 2em;width:11.8em}.location:focus{outline:none}.icon{font-family:"Meteocons", sans-serif}.icon.big{font-size:8em;float:right}.or{vertical-align:3px;opacity:0.4}.bottom{margin-top:0.9em}.bottom .icon{font-size:4em;margin-right:0.15em;vertical-align:bottom;position:relative;top:0.15em;float:left}.bottom:after{visibility:hidden;display:block;content:"";clear:both;height:0}.left{float:left;width:49.8%}.right{float:right;width:49.8%}.info{font-size:1.2em;vertical-align:middle}footer{text-align:center;margin:3.5em 0 2em}footer>a{color:#bababa;text-decoration:none} 2 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmilton/Simple-Weather-App/01b3b52484d93ee8c8837c068f413365199002a3/app/favicon.ico -------------------------------------------------------------------------------- /app/fonts/meteocons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmilton/Simple-Weather-App/01b3b52484d93ee8c8837c068f413365199002a3/app/fonts/meteocons.eot -------------------------------------------------------------------------------- /app/fonts/meteocons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | This is a custom SVG webfont generated by Font Squirrel. 6 | Designer : Alessio Atzeni 7 | Foundry : Alessio Atzeni 8 | Foundry URL : http://www.alessioatzeni.com/meteocons/ 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /app/fonts/meteocons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmilton/Simple-Weather-App/01b3b52484d93ee8c8837c068f413365199002a3/app/fonts/meteocons.ttf -------------------------------------------------------------------------------- /app/fonts/meteocons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmilton/Simple-Weather-App/01b3b52484d93ee8c8837c068f413365199002a3/app/fonts/meteocons.woff -------------------------------------------------------------------------------- /app/fonts/meteocons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmilton/Simple-Weather-App/01b3b52484d93ee8c8837c068f413365199002a3/app/fonts/meteocons.woff2 -------------------------------------------------------------------------------- /app/fonts/roboto-light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmilton/Simple-Weather-App/01b3b52484d93ee8c8837c068f413365199002a3/app/fonts/roboto-light.eot -------------------------------------------------------------------------------- /app/fonts/roboto-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | -------------------------------------------------------------------------------- /app/fonts/roboto-light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmilton/Simple-Weather-App/01b3b52484d93ee8c8837c068f413365199002a3/app/fonts/roboto-light.ttf -------------------------------------------------------------------------------- /app/fonts/roboto-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmilton/Simple-Weather-App/01b3b52484d93ee8c8837c068f413365199002a3/app/fonts/roboto-light.woff -------------------------------------------------------------------------------- /app/fonts/roboto-light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmilton/Simple-Weather-App/01b3b52484d93ee8c8837c068f413365199002a3/app/fonts/roboto-light.woff2 -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Simple Weather App 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 23 | 24 |
25 |

Simple Weather App

26 |

A simple experiment using AngularJS, various APIs, and CSS3.

27 |

Source code available on Github.

28 |
29 | 30 |
31 |

32 | 33 |

34 | 35 |
36 |
37 | {{ main.icon }} 38 |

{{ main.kelvinToCelcius(main.dataCurrent.main.temp) }} °C

39 |

{{ main.dataCurrent.weather[0].description }}

40 |

▲ {{ main.kelvinToCelcius(main.dataDaily.list[0].temp.max) }} °C / ▼ {{ main.kelvinToCelcius(main.dataDaily.list[0].temp.min) }} °C

41 |
42 | 43 |
44 | 47 | or 48 | 49 |
50 | 51 |
52 |
53 | F 54 |

Wind speed: {{ main.dataCurrent.wind.speed }} m/s

55 |

Humidity: {{ main.dataCurrent.main.humidity }}%

56 |
57 |
58 | A 59 |

Sunrise: {{ main.dataCurrent.sys.sunrise * 1000 | date : 'shortTime' }}

60 |

Sunset: {{ main.dataCurrent.sys.sunset * 1000 | date : 'shortTime' }}

61 |
62 |
63 |
64 | 65 | 66 |
67 |

Coordinates: [{{ main.lat }}, {{ main.lon }}]

68 |
69 |
70 |
71 | 72 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /app/js/angular-animate.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.5.0-rc.0 3 | (c) 2010-2015 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(J,t,Ta){'use strict';function xa(a,b,c){if(!a)throw ngMinErr("areq",b||"?",c||"required");return a}function ya(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;Z(a)&&(a=a.join(" "));Z(b)&&(b=b.join(" "));return a+" "+b}function Ka(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function U(a,b,c){var d="";a=Z(a)?a:a&&H(a)&&a.length?a.split(/\s+/):[];w(a,function(a,r){a&&0=a&&(a=k,k=0,b.push(f),f=[]);f.push(h.fn); 27 | h.children.forEach(function(a){k++;c.push(a)});a--}f.length&&b.push(f);return b}(c)}var ea=[],t=O(a);return function(g,u,C){function ba(a){a=a.hasAttribute("ng-animate-ref")?[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];w(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&b.push(a)});return b}function s(a){var b=[],c={};w(a,function(a,e){var d=G(a.element),k=0<=["enter","move"].indexOf(a.event),d=a.structural?ba(d):[];if(d.length){var f=k?"to":"from";w(d,function(a){var b=a.getAttribute("ng-animate-ref"); 28 | c[b]=c[b]||{};c[b][f]={animationID:e,element:L(a)}})}else b.push(a)});var e={},d={};w(c,function(c,k){var f=c.from,z=c.to;if(f&&z){var n=a[f.animationID],p=a[z.animationID],h=f.animationID.toString();if(!d[h]){var B=d[h]={structural:!0,beforeStart:function(){n.beforeStart();p.beforeStart()},close:function(){n.close();p.close()},classes:y(n.classes,p.classes),from:n,to:p,anchors:[]};B.classes.length?b.push(B):(b.push(n),b.push(p))}d[h].anchors.push({out:f.element,"in":z.element})}else f=f?f.animationID: 29 | z.animationID,z=f.toString(),e[z]||(e[z]=!0,b.push(a[f]))});return b}function y(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],e=0;ev.expectedEndTime)?A.cancel(v.timer):g.push(k)}m&&(s=A(c,s,!1),g[0]={timer:s,expectedEndTime:p},g.push(k),a.data("$$animateCss",g));a.on(n.join(" "),d);e.to&&(e.cleanupStyles&&Fa(u,l,Object.keys(e.to)),Aa(a,e))}}function c(){var b=a.data("$$animateCss");if(b){for(var e=1;e=O&&b>=K&&(J=!0,k())}if(!x)if(l.parentNode){var h,n=[],p=function(a){if(J)ca&&a&&(ca=!1,k());else if(ca=!a,F.animationDuration)if(a=pa(l,ca),ca)y.push(a);else{var b=y,c=b.indexOf(a);0<=a&&b.splice(c,1)}},g=0<$&&(F.transitionDuration&&0===S.transitionDuration||F.animationDuration&&0===S.animationDuration)&&Math.max(S.animationDelay,S.transitionDelay);g?A(b,Math.floor(g*$*1E3),!1):b();L.resume=function(){p(!0)};L.pause=function(){p(!1)}}else k()}var e=Ga(c),u={},l=G(a);if(!l||!l.parentNode||!t.enabled())return s(); 39 | var e=ma(e),y=[],v=a.attr("class"),D=Ka(e),x,ca,J,m,L,H,O,K,R;if(0===e.duration||!h.animations&&!h.transitions)return s();var ga=e.event&&Z(e.event)?e.event.join(" "):e.event,V="",Q="";ga&&e.structural?V=U(ga,"ng-",!0):ga&&(V=ga);e.addClass&&(Q+=U(e.addClass,"-add"));e.removeClass&&(Q.length&&(Q+=" "),Q+=U(e.removeClass,"-remove"));e.applyClassesEarly&&Q.length&&ba(a,e);var da=[V,Q].join(" ").trim(),ka=v+" "+da,aa=U(da,"-active"),v=D.to&&0'); 69 | 70 | // Set the OpenWeatherMap API URls (via a http to https proxy) 71 | _apiUrlCurrent = prepareUri('https://jsonp.afeld.me/?url=' + 'http://api.openweathermap.org/data/2.5/weather?lat=' + round(vm.lat) + '&lon=' + round(vm.lon) + '&APPID=' + _apiKey + '&callback=JSON_CALLBACK'); 72 | _apiUrlDaily = prepareUri('https://jsonp.afeld.me/?url=' + 'http://api.openweathermap.org/data/2.5/forecast/daily?lat=' + round(vm.lat) + '&lon=' + round(vm.lon) + '&cnt=1&APPID=' + _apiKey + '&callback=JSON_CALLBACK'); 73 | 74 | // Once we have the location request the weather data 75 | vm.weather.current(); 76 | vm.weather.daily(); 77 | }; 78 | 79 | function error(err) { 80 | $log.error("navigator.geolocation error: ", err); 81 | }; 82 | }; 83 | 84 | vm.weather = { 85 | current: function() { 86 | // Do a JSONP AJAX request to get current weather data, use caching for performance 87 | $http.jsonp(_apiUrlCurrent, {cache: true}) 88 | .success(function(data) { 89 | 90 | // $log.debug("Current weather data: %O", data); 91 | vm.dataCurrent = data; 92 | 93 | // Set search/location input to show current location 94 | vm.searchBox = vm.dataCurrent.name + ', ' + vm.dataCurrent.sys.country; 95 | 96 | // Show relevant icon 97 | vm.showIcon(); 98 | }) 99 | .error(function(err) { 100 | $log.error("$http error: ", err); 101 | }); 102 | }, 103 | daily: function() { 104 | // Do a JSONP AJAX request to get daily weather data, use caching for performance 105 | $http.jsonp(_apiUrlDaily, { cache: true }) 106 | .success(function(data) { 107 | // $log.debug("Daily weather data: ", data); 108 | vm.dataDaily = data; 109 | }) 110 | .error(function(err) { 111 | $log.error("$http error: ", err); 112 | }); 113 | } 114 | }; 115 | 116 | vm.showIcon = function() { 117 | // Choose the cosponsoring icon font letter to the correct weather type 118 | switch (vm.dataCurrent.weather[0].icon) { 119 | case '01d': 120 | // Clear sky, day 121 | vm.icon = 'B'; 122 | vm.changeColour('orange'); 123 | break; 124 | case '01n': 125 | // Clear sky, night 126 | vm.icon = 'C'; 127 | vm.changeColour('purple'); 128 | break; 129 | case '02d': 130 | // Few clouds, day 131 | vm.icon = 'H'; 132 | vm.changeColour('orange'); 133 | break; 134 | case '02n': 135 | // Few clouds, night 136 | vm.icon = 'I'; 137 | vm.changeColour('purple'); 138 | break; 139 | case '03d': 140 | case '03n': 141 | // Scattered clouds 142 | vm.icon = 'N'; 143 | vm.changeColour('green'); 144 | break; 145 | case '04d': 146 | case '04n': 147 | // Broken clouds 148 | vm.icon = 'Y'; 149 | vm.changeColour('green'); 150 | break; 151 | case '09d': 152 | case '09n': 153 | // Shower rain 154 | vm.icon = 'R'; 155 | vm.changeColour('blue'); 156 | break; 157 | case '10d': 158 | case '10n': 159 | // Rain 160 | vm.icon = 'Q'; 161 | vm.changeColour('blue'); 162 | break; 163 | case '11d': 164 | case '11n': 165 | // Thunderstorm 166 | vm.icon = 'O'; 167 | vm.changeColour('blue'); 168 | break; 169 | case '13d': 170 | case '13n': 171 | // Snow 172 | vm.icon = 'W'; 173 | vm.changeColour('grey'); 174 | break; 175 | case '50d': 176 | case '50n': 177 | // Mist 178 | vm.icon = 'M'; 179 | vm.changeColour('grey'); 180 | break; 181 | default: 182 | // Unsure of weather 183 | vm.icon = ')'; 184 | vm.changeColour('grey'); 185 | }; 186 | }; 187 | 188 | vm.changeColour = function (color) { 189 | // Change the colour of the .card CSS cl 190 | // Declare the controllers public APIass 191 | vm.cardColour = color; 192 | }; 193 | 194 | vm.searchClick = function() { 195 | // Clear the contents of the search box but save the previous contents 196 | _currentLocation = vm.searchBox; 197 | vm.searchBox = ''; 198 | }; 199 | 200 | vm.searchBlur = function() { 201 | // If the user didn't type a location restore the previous one 202 | if (vm.searchBox === '') { 203 | vm.searchBox = _currentLocation; 204 | }; 205 | }; 206 | 207 | vm.randomCity = function () { 208 | // Bunch of cities for random selection 209 | var cities = [ 210 | 'Sydney, AU', 211 | 'Melbourne, AU', 212 | 'Tokyo', 213 | 'Osaka', 214 | 'Seoul', 215 | 'Hong Kong', 216 | 'London', 217 | 'Amsterdam', 218 | 'Berlin', 219 | 'Paris', 220 | 'Barcelona', 221 | 'New York', 222 | 'Dubai', 223 | 'Antarctica' 224 | ]; 225 | 226 | // Use a loop to prevent the same city as current (confusing for UX) 227 | do { 228 | // Generate a random number up to the array size 229 | var random = Math.floor(Math.random() * cities.length); 230 | } while (cities[random] === vm.searchBox); 231 | 232 | // Run initial search to populate the view 233 | vm.search(cities[random]); 234 | }; 235 | }]); 236 | })(); 237 | -------------------------------------------------------------------------------- /app/scss/_animations.scss: -------------------------------------------------------------------------------- 1 | ////////////// 2 | // pullDown // 3 | ////////////// 4 | .pull-down{ 5 | animation-name: pullDown; 6 | -webkit-animation-name: pullDown; 7 | 8 | animation-duration: $speed; 9 | -webkit-animation-duration: $speed; 10 | 11 | animation-timing-function: ease-out; 12 | -webkit-animation-timing-function: ease-out; 13 | 14 | transform-origin: 50% 0%; 15 | -ms-transform-origin: 50% 0%; 16 | -webkit-transform-origin: 50% 0%; 17 | } 18 | 19 | @keyframes pullDown { 20 | 0% { 21 | transform: scaleY(0.1); 22 | } 23 | 100% { 24 | transform: scaleY(1); 25 | } 26 | } 27 | 28 | @-webkit-keyframes pullDown { 29 | 0% { 30 | -webkit-transform: scaleY(0.1); 31 | } 32 | 100% { 33 | -webkit-transform: scaleY(1); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/scss/_fonts.scss: -------------------------------------------------------------------------------- 1 | /////////////// 2 | // Variables // 3 | /////////////// 4 | $FontPath: "../fonts" !default; 5 | 6 | //////////// 7 | // Mixins // 8 | //////////// 9 | @mixin fontdef-woff($FontPath, $FontName) { 10 | src: url('#{$FontPath}/#{$FontName}.eot'); 11 | src: url('#{$FontPath}/#{$FontName}.eot?#iefix') format('eot'), 12 | url('#{$FontPath}/#{$FontName}.woff2') format('woff2'), 13 | url('#{$FontPath}/#{$FontName}.woff') format('woff'), 14 | url('#{$FontPath}/#{$FontName}.ttf') format('truetype'); 15 | } 16 | 17 | ///////////////////////////// 18 | // Roboto font declaration // 19 | ///////////////////////////// 20 | @font-face { 21 | font-family: Roboto; 22 | @include fontdef-woff($FontPath, "roboto-light"); 23 | font-weight: 300; 24 | font-style: normal; 25 | } 26 | 27 | /////////////////////////// 28 | // Icon font declaration // 29 | /////////////////////////// 30 | @font-face { 31 | font-family: Meteocons; 32 | @include fontdef-woff($FontPath, "meteocons"); 33 | font-weight: normal; 34 | font-style: normal; 35 | 36 | text-rendering: auto; // Cross browser compatibility 37 | -moz-osx-font-smoothing: grayscale; 38 | transform: translate(0, 0); // Ensures no half-pixel rendering in firefox 39 | } 40 | -------------------------------------------------------------------------------- /app/scss/_normalize.scss: -------------------------------------------------------------------------------- 1 | /* normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } 428 | -------------------------------------------------------------------------------- /app/scss/theme.scss: -------------------------------------------------------------------------------- 1 | /////////////// 2 | // Variables // 3 | /////////////// 4 | // Fonts 5 | $font-family: Roboto, "Roboto", sans-serif !default; 6 | $font-weight: 300 !default; 7 | 8 | // Colours 9 | $white: #FFF; 10 | $black: #212121; 11 | $grey-bg: #E0E0E0; 12 | $orange: #F57C00; // Orange 700 13 | $orange-light: #FF9800; // Orange 500 14 | $blue: #1976D2; // Blue 700 15 | $blue-light: #2196F3; // Blue 500 16 | $purple: #512DA8; // Purple 700 17 | $purple-light: #673AB7; // Purple 500 18 | $green: #388E3C; // Green 700 19 | $green-light: #4CAF50; // Green 500 20 | $grey: #455A64; // Grey 700 21 | $grey-light: #607D8B; // Grey 500 22 | 23 | // Misc 24 | $radius: 5px !default; 25 | $speed: 0.7s !default; 26 | 27 | 28 | ///////////// 29 | // Imports // 30 | ///////////// 31 | @import 'normalize'; 32 | @import 'fonts'; 33 | @import 'animations'; 34 | 35 | 36 | /////////////////////// 37 | // AngularJS Helpers // 38 | /////////////////////// 39 | [ng-cloak] { 40 | display: none !important; 41 | } 42 | 43 | 44 | /////////////////// 45 | // Global Styles // 46 | /////////////////// 47 | html { 48 | background-color: $grey-bg; 49 | color: $black; 50 | font: $font-weight 1.2em $font-family; 51 | margin-left: 1em; 52 | margin-right: 1em; 53 | } 54 | 55 | header { margin-bottom: 2em; } 56 | 57 | h1 { 58 | font-size: 2.3em; 59 | font-weight: $font-weight; 60 | } 61 | 62 | .lead { font-size: 1.3em; } 63 | 64 | button { 65 | background-color: transparent; 66 | font-family: $font-family; 67 | font-size: 1.2em; 68 | font-weight: $font-weight; 69 | border: none; 70 | padding: 0.4em; 71 | opacity: 0.6; 72 | 73 | &:focus { 74 | outline:none; 75 | } 76 | } 77 | 78 | .btn-random { 79 | box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.2); 80 | border-radius: $radius; 81 | 82 | &:hover { 83 | box-shadow: 1px 2px 8px rgba(0, 0, 0, 0.4); 84 | } 85 | 86 | & > .icon { 87 | vertical-align: -2px; 88 | } 89 | } 90 | 91 | .btn-locate:hover { 92 | opacity: 1; 93 | } 94 | 95 | .card { 96 | // Animate background colour changes 97 | transition: background-color $speed ease; 98 | 99 | color: $white; 100 | padding: 1em; 101 | border-radius: $radius; 102 | box-shadow: 0 0 15px rgba(0, 0, 0, 0.1); 103 | max-width: 30em; 104 | position: relative; 105 | z-index: 2; 106 | 107 | &.orange { 108 | background-color: $orange-light; 109 | 110 | .location-panel { 111 | background-color: $orange; 112 | } 113 | 114 | & + .card { 115 | color: $orange; 116 | } 117 | } 118 | 119 | &.blue { 120 | background-color: $blue-light; 121 | 122 | .location-panel { 123 | background-color: $blue; 124 | } 125 | 126 | & + .card { 127 | color: $blue; 128 | } 129 | } 130 | 131 | &.purple { 132 | background-color: $purple-light; 133 | 134 | .location-panel { 135 | background-color: $purple; 136 | } 137 | 138 | & + .card { 139 | color: $purple; 140 | } 141 | } 142 | 143 | &.grey { 144 | background-color: $grey-light; 145 | 146 | .location-panel { 147 | background-color: $grey; 148 | } 149 | 150 | & + .card { 151 | color: $grey; 152 | } 153 | } 154 | 155 | &.green { 156 | background-color: $green-light; 157 | 158 | .location-panel { 159 | background-color: $green; 160 | } 161 | 162 | & + .card { 163 | color: $green; 164 | } 165 | } 166 | 167 | & + .card { 168 | background-color: $white; 169 | padding-top: 0.6em; 170 | margin-top: -0.5em; 171 | z-index: 1; 172 | 173 | & > p { 174 | // Animate text colour changes 175 | transition: color $speed ease; 176 | } 177 | } 178 | } 179 | 180 | .top { padding: 0.3em 1em 1em; } 181 | 182 | .temp-now { 183 | font-size: 6em; 184 | margin: 0; 185 | 186 | & > span { 187 | font-size: 0.6em; 188 | vertical-align: 0.48em; 189 | } 190 | } 191 | 192 | .weather-now { 193 | font-size: 2em; 194 | text-transform: capitalize; 195 | margin: 0.1em 0 0.2em; 196 | } 197 | 198 | .highlow { 199 | font-size: 1.2em; 200 | margin: 0 0 0.2em; 201 | } 202 | 203 | .location-panel { 204 | // Animate background colour changes 205 | transition: background-color $speed ease; 206 | font-size: 1.1em; 207 | border-radius: $radius; 208 | } 209 | 210 | .search { display: inline; } 211 | 212 | .location { 213 | // Inline SVG: Search icon 214 | background: url('data:image/svg+xml;utf8,') 0.4em 50% no-repeat; 215 | color: $white; 216 | font-family: $font-family; 217 | font-size: 1.4em; 218 | font-weight: $font-weight; 219 | background-color: transparent; 220 | border: none; 221 | padding: 0.4em 0 0.4em 2em; 222 | width: 11.8em; 223 | 224 | &:focus { 225 | outline:none; 226 | } 227 | } 228 | 229 | .icon { 230 | font-family: "Meteocons", sans-serif; 231 | 232 | &.big { 233 | font-size: 8em; 234 | float: right; 235 | } 236 | } 237 | 238 | .or { 239 | vertical-align: 3px; 240 | opacity: 0.4; 241 | } 242 | 243 | .bottom { 244 | margin-top: 0.9em; 245 | 246 | .icon { 247 | font-size: 4em; 248 | margin-right: 0.15em; 249 | vertical-align: bottom; 250 | position: relative; 251 | top: 0.15em; 252 | float: left; 253 | } 254 | 255 | &:after { 256 | // CLearfix hack 257 | visibility: hidden; 258 | display: block; 259 | content: ""; 260 | clear: both; 261 | height: 0; 262 | } 263 | } 264 | 265 | .left { 266 | float: left; 267 | width: 49.8%; 268 | } 269 | 270 | .right { 271 | float: right; 272 | width: 49.8%; 273 | } 274 | 275 | .info { 276 | font-size: 1.2em; 277 | vertical-align: middle; 278 | } 279 | 280 | footer { 281 | text-align: center; 282 | margin: 3.5em 0 2em; 283 | 284 | & > a { 285 | color: darken($grey-bg, 15%); 286 | text-decoration: none; 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-weather-app", 3 | "version": "1.0.0", 4 | "description": "A simple experiment using AngularJS, the OpenWeatherMap API, and CSS3.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "http-server -a 127.0.0.1 -p 8880 ./app", 8 | "watch": "node-sass -w --output-style compressed app/scss/theme.scss -o app/css/", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/MaxMilton/Simple-Weather-App.git" 14 | }, 15 | "author": "Max Milton ", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/MaxMilton/Simple-Weather-App/issues" 19 | }, 20 | "homepage": "https://github.com/MaxMilton/Simple-Weather-App#readme", 21 | "devDependencies": { 22 | "http-server": "^0.8.5", 23 | "node-sass": "^3.4.2" 24 | } 25 | } 26 | --------------------------------------------------------------------------------