├── index.html ├── scripts └── index.js └── styles └── index.processed.css /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | My New Pen! 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

Latest weather at Elysium Plantitia

22 |
23 |

Sol 

24 |

25 |
26 | 27 |
28 |

Temperature

29 |

30 | High: 31 | 32 | °C 33 |

34 |

35 | Low: 36 | 37 | °C 38 |

39 |
40 | 41 |
42 |

Wind

43 |

44 | 45 | kph 46 |

47 |
48 |

deg

49 |
50 |
51 |
52 | 53 |
54 |

InSight is taking daily weather measurements (temperature, wind, pressure) on the surface of Mars at Elysium Planitia, a flat, smooth plain near Mars’ equator.

55 |

This is only a part of InSight’s mission. Click here to find out more.

56 |
57 | 58 |
59 | °C 60 | 67 | °F 68 |
69 | 70 |
71 | 72 |
73 | 74 | 75 |

Previous 7 days

76 | 77 |
78 |
79 | 80 | 97 | 98 | -------------------------------------------------------------------------------- /scripts/index.js: -------------------------------------------------------------------------------- 1 | const API_KEY = 'DEMO_KEY' 2 | const API_URL = `https://api.nasa.gov/insight_weather/?api_key=${API_KEY}&feedtype=json&ver=1.0` 3 | 4 | const currentSolElement = document.querySelector('[data-current-sol]') 5 | const currentDateElement = document.querySelector('[data-current-date]') 6 | const currentTempHighElement = document.querySelector('[data-current-temp-high]') 7 | const currentTempLowElement = document.querySelector('[data-current-temp-low]') 8 | const windSpeedElement = document.querySelector('[data-wind-speed]') 9 | const windDirectionText = document.querySelector('[data-wind-direction-text]') 10 | const windDirectionArrow = document.querySelector('[data-wind-direction-arrow]') 11 | 12 | const previousSolTemplate = document.querySelector('[data-previous-sol-template]') 13 | const previousSolContainer = document.querySelector('[data-previous-sols]') 14 | 15 | const previousWeatherToggle = document.querySelector('[data-previous-weather-toggle]') 16 | const previousWeatherContainer = document.querySelector('[data-previous-weather-container]') 17 | 18 | const unitToggle = document.querySelector('[data-unit-toggle]') 19 | 20 | let selectedSolIndex 21 | let metricUnits = unitToggle.getAttribute('aria-checked') !== 'true' 22 | 23 | previousWeatherToggle.addEventListener('change', () => { 24 | previousWeatherContainer.classList.toggle('show-weather') 25 | }) 26 | 27 | getWeather().then(sols => { 28 | selectedSolIndex = sols.length - 1 29 | displayPreviousSols(sols) 30 | displaySelectedSol(sols) 31 | 32 | unitToggle.addEventListener('click', () => { 33 | metricUnits = !metricUnits 34 | const label = metricUnits ? "celsius" : "fahrenheit" 35 | unitToggle.setAttribute('aria-checked', !metricUnits) 36 | unitToggle.setAttribute('aria-label', label) 37 | displaySols(sols) 38 | updateUnits() 39 | }) 40 | }) 41 | 42 | function getWeather() { 43 | return fetch(API_URL) 44 | .then(res => res.json()) 45 | .then(resData => { 46 | const { 47 | sol_keys, 48 | validity_checks, 49 | ...solData 50 | } = resData 51 | return Object.entries(solData).map(([sol, data]) => { 52 | return { 53 | sol: sol, 54 | maxTemp: data.AT.mx, 55 | minTemp: data.AT.mn, 56 | windSpeed: data.HWS.av, 57 | windDirectionDegrees: data.WD.most_common.compass_degrees, 58 | windDirectionCardinal: data.WD.most_common.compass_point, 59 | date: new Date(data.First_UTC) 60 | } 61 | }) 62 | }) 63 | } 64 | 65 | function displaySols(sols) { 66 | displaySelectedSol(sols) 67 | displayPreviousSols(sols) 68 | } 69 | 70 | function displaySelectedSol(sols) { 71 | const selectedSol = sols[selectedSolIndex] 72 | currentSolElement.innerText = selectedSol.sol 73 | currentDateElement.innerText = displayDate(selectedSol.date) 74 | currentTempHighElement.innerText = displayTemperature(selectedSol.maxTemp) 75 | currentTempLowElement.innerText = displayTemperature(selectedSol.minTemp) 76 | windSpeedElement.innerText = displaySpeed(selectedSol.windSpeed) 77 | windDirectionText.innerText = selectedSol.windDirectionCardinal 78 | windDirectionArrow.style.setProperty('--direction', `${selectedSol.windDirectionDegrees}deg`) 79 | } 80 | 81 | function displayPreviousSols(sols) { 82 | previousSolContainer.innerHTML = '' 83 | sols.forEach((solData, index) => { 84 | const solContainer = previousSolTemplate.content.cloneNode(true) 85 | solContainer.querySelector('[data-sol]').innerText = solData.sol 86 | solContainer.querySelector('[data-date]').innerText = displayDate(solData.date) 87 | solContainer.querySelector('[data-temp-high]').innerText = displayTemperature(solData.maxTemp) 88 | solContainer.querySelector('[data-temp-low]').innerText = displayTemperature(solData.minTemp) 89 | solContainer.querySelector('[data-select-button]').addEventListener('click', () => { 90 | selectedSolIndex = index 91 | displaySelectedSol(sols) 92 | }) 93 | previousSolContainer.appendChild(solContainer) 94 | }) 95 | } 96 | 97 | function displayDate(date) { 98 | return date.toLocaleDateString( 99 | undefined, 100 | { day: 'numeric', month: 'long' } 101 | ) 102 | } 103 | 104 | function displayTemperature(temperature) { 105 | let returnTemp = temperature 106 | if (!metricUnits) { 107 | returnTemp = (temperature - 32) * (5 / 9) 108 | } 109 | 110 | return Math.round(returnTemp) 111 | } 112 | 113 | function displaySpeed(speed) { 114 | let returnSpeed = speed 115 | if (!metricUnits) { 116 | returnSpeed = speed / 1.609 117 | } 118 | 119 | return Math.round(returnSpeed) 120 | } 121 | 122 | function updateUnits() { 123 | const speedUnits = document.querySelectorAll('[data-speed-unit]') 124 | const tempUnits = document.querySelectorAll('[data-temp-unit]') 125 | speedUnits.forEach(unit => unit.innerText = metricUnits ? 'kph' : 'mph') 126 | tempUnits.forEach(unit => unit.innerText = metricUnits ? 'C' : 'F') 127 | } -------------------------------------------------------------------------------- /styles/index.processed.css: -------------------------------------------------------------------------------- 1 | *, *::before, *::after { 2 | box-sizing: border-box; 3 | } 4 | 5 | :root { 6 | --fw-light: 300; 7 | --fw-normal: 400; 8 | --fw-semi: 500; 9 | --fw-bold: 700; 10 | --fs-h1: 1.5rem; 11 | --fs-h2: 2.25rem; 12 | --fs-body: 1rem; 13 | --fs-xl: 4.5rem; 14 | --clr-light: #fff; 15 | --clr-gray: #989898; 16 | --clr-dark: #444; 17 | --clr-accent: #D06D6D; 18 | --clr-accent-dark: #613131; 19 | } 20 | 21 | body { 22 | margin: 0; 23 | font-family: 'Montserrat', sans-serif; 24 | line-height: 1.6; 25 | background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/308367/mars.jpg); 26 | background-size: cover; 27 | height: 100vh; 28 | overflow: hidden; 29 | color: var(--clr-gray); 30 | } 31 | 32 | .sr-only:not(:focus):not(:active) { 33 | clip: rect(0 0 0 0); 34 | clip-path: inset(50%); 35 | height: 1px; 36 | overflow: hidden; 37 | position: absolute; 38 | white-space: nowrap; 39 | width: 1px; 40 | } 41 | 42 | h1, h2 { 43 | line-height: 1; 44 | } 45 | 46 | .main-title { 47 | font-size: var(--fs-h1); 48 | font-weight: var(--fw-light); 49 | color: var(--clr-accent); 50 | letter-spacing: 2px; 51 | text-transform: uppercase; 52 | margin: 0 0 .5em 0; 53 | } 54 | 55 | .section-title { 56 | font-size: var(--fs-h2); 57 | font-weight: var(--fw-bold); 58 | color: var(--clr-light); 59 | margin: 0; 60 | } 61 | 62 | .section-title--date { 63 | font-size: var(--fs-xl); 64 | } 65 | 66 | a { 67 | color: var(--clr-accent); 68 | } 69 | 70 | a:hover { 71 | color: var(--clr-light); 72 | } 73 | 74 | .mars-current-weather { 75 | background: rgba(0, 0, 0, 0.75); 76 | padding: 2em; 77 | margin: 4em 0 0 4em; 78 | display: grid; 79 | grid-template-columns: repeat(3, 1fr); 80 | grid-gap: 2em; 81 | max-width: 1000px; 82 | } 83 | 84 | .main-title { 85 | grid-column: 1 / -1; 86 | } 87 | 88 | .reading { 89 | font-size: var(--fs-h1); 90 | line-height: 1.4; 91 | margin: 0; 92 | color: var(--clr-gray); 93 | } 94 | 95 | .date__day { 96 | font-size: var(--fs-h2); 97 | color: var(--clr-gray); 98 | line-height: 1; 99 | margin: 0; 100 | font-weight: var(--fw-light); 101 | } 102 | 103 | .temp { 104 | border-left: solid 0.25em var(--clr-accent-dark); 105 | border-right: solid 0.25em var(--clr-accent-dark); 106 | padding: 0 2em; 107 | } 108 | 109 | .wind { 110 | display: grid; 111 | grid-column: repeat(2, 1fr); 112 | grid-column-gap: 1rem; 113 | justify-content: start; 114 | align-items: start; 115 | } 116 | 117 | .wind .section-title, .wind .reading { 118 | grid-column: 2 / 3; 119 | margin: 0; 120 | } 121 | 122 | .wind__direction { 123 | --size: 6rem; 124 | width: var(--size); 125 | height: var(--size); 126 | border-radius: 50%; 127 | background: rgba(255, 255, 255, 0.5); 128 | grid-row: 1 / 3; 129 | grid-column: 1 / 2; 130 | display: grid; 131 | place-items: center; 132 | } 133 | 134 | .wind__arrow { 135 | --direction: 0deg; 136 | --size: 1rem; 137 | height: calc(var(--size) * 3); 138 | width: var(--size); 139 | background: var(--clr-accent-dark); 140 | clip-path: polygon(50% 0, 0% 100%, 100% 100%); 141 | transform: translateY(-50%) rotate(var(--direction)); 142 | transform-origin: bottom center; 143 | transition: transform 500ms cubic-bezier(0, .5, .5, 1); 144 | } 145 | 146 | .unit { 147 | place-self: end; 148 | display: flex; 149 | } 150 | 151 | .unit__toggle { 152 | cursor: pointer; 153 | margin: 0 1em; 154 | width: 4em; 155 | border: 2px solid var(--clr-gray); 156 | background: transparent; 157 | border-radius: 100vw; 158 | position: relative; 159 | display: flex; 160 | padding: 0; 161 | transition: border-color 250ms; 162 | } 163 | 164 | .unit__toggle:hover { 165 | --clr-gray: #fff; 166 | } 167 | 168 | .unit__toggle::after { 169 | content: ''; 170 | background: var(--clr-gray); 171 | border-radius: 100vmax; 172 | margin: 3px; 173 | height: 15px; 174 | width: calc(50% - 8px); 175 | transition: background-color 250ms; 176 | } 177 | 178 | .unit__toggle[aria-checked="true"]::after { 179 | margin-left: auto; 180 | } 181 | 182 | .info { 183 | grid-column: 1 / 3; 184 | color: var(clr-light); 185 | } 186 | 187 | .previous-weather { 188 | background: white; 189 | padding: 2rem 4rem; 190 | color: var(--clr-gray); 191 | position: absolute; 192 | bottom: 0; 193 | transform: translateY(60%); 194 | width: 100%; 195 | transition: transform 350ms ease; 196 | } 197 | 198 | .show-previous-weather-toggle { 199 | display: none; 200 | } 201 | 202 | .show-previous-weather-label { 203 | position: absolute; 204 | background: var(--clr-light); 205 | top: 0; 206 | /* 9rem = width */ 207 | transform: translate(calc(50% - 9rem), -100%); 208 | left: 50%; 209 | border: 0; 210 | width: 9rem; 211 | height: 2rem; 212 | display: grid; 213 | place-items: center; 214 | clip-path: polygon(50% 0, 0% 100%, 100% 100%); 215 | font-size: 2em; 216 | line-height: 1; 217 | font-weight: var(--fw-semi); 218 | transition: transform 200ms ease; 219 | } 220 | 221 | .previous-days { 222 | display: flex; 223 | justify-content: space-between; 224 | opacity: 0; 225 | transform: translateY(100px); 226 | transition: opacity 250ms linear 200ms, transform 750ms cubic-bezier(0, 0.5, 0.5, 1); 227 | } 228 | 229 | .previous-day > * { 230 | margin: 0; 231 | } 232 | 233 | .previous-day__sol { 234 | color: var(--clr-dark); 235 | } 236 | 237 | .show-weather { 238 | transform: translateY(0); 239 | } 240 | 241 | .show-weather .previous-days { 242 | opacity: 1; 243 | transform: translateY(0); 244 | } 245 | 246 | .show-weather label * { 247 | transform: rotate(180deg) translateY(-.15em); 248 | transform-origin: center; 249 | } 250 | 251 | 252 | .more-info { 253 | cursor: pointer; 254 | margin-top: 1em; 255 | border: 0; 256 | border-radius: 100vw; 257 | background: var(--clr-gray); 258 | color: var(--clr-light); 259 | text-transform: uppercase; 260 | padding: .25em .75em; 261 | } 262 | 263 | .more-info:hover { 264 | background: var(--clr-accent-dark) 265 | } --------------------------------------------------------------------------------