// General layout and styling
body {
text-align: center;
display: grid;
place-items: center;
margin: 0 15vw;
.box {
width: 100%;
margin:6vh 0 !important;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
height: 30vh;
}
.outer {
height: 40vh;
width:60vw;
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
align-items: center;
}
.inner {
max-width:50%;
margin:0 .5vw !important;
padding: 2%;
display: flex;
justify-content: center;
flex-direction: column;
flex: 1 0 auto;
align-self: center;
overflow-y:scroll;
}
.top {
margin-bottom: 2vh;
height: 8vh;
width:100%;
background-color:rgba(0,0,0,0.2);
font-size: 20px;
flex: 1 0 auto;
align-self: center;
}
.bottom {
width:100%;
height: 8vh;
background-color:rgba(0,0,0,0.2);
flex: 1 0 auto;
align-self: center;
}
}
// Skeleton Layout styles and animations
.loading {
height: 30vh;
position: relative;
background: #cccccc;
}
.loading:after {
content: "";
display: block;
position: absolute;
top:0;
width: 100%;
height: 100%;
transform: translateX(-100px);
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
animation: loading 0.8s infinite;
}
@keyframes loading {
100% {
transform: translateX(100%);
}
}
// Loaded image styling
.loaded {
color: white;
flex: 1 0 auto;
}
.loaded .top, .loaded .bottom {
background: none;
}
View Compiled
const API_KEY = 'FB9WxicEGwNrMQaPyvXTpUDpgOv_T1HMUGptR4AT4hY';
class App extends React.Component {
constructor(props){
super(props);
this.state = {
isLoaded: false,
images: []
}
};
render(){
const loadImages = () => {
//Fetch Unsplash images after 'Load Photos' button is clicked
fetch('https://api.unsplash.com/photos/?client_id=' + API_KEY)
.then(res => res.json())
.then(data => {
this.setState({ images: data });
this.setState({isLoaded: true});
})
.catch(err => {
console.log('Error : ', err);
});
};
if(this.state.isLoaded===true){
// If 'Load Photos' button is clicked and images are fetched,
// then app will render photos/content
let random1 = Math.floor(Math.random() * 10)
let random2 = Math.floor(Math.random() * 10)
let random3 = Math.floor(Math.random() * 10)
return(
<>
<div className="box loaded" style={{ backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.5),
rgba(0, 0, 0, 0.5)), url(${this.state.images[random1].urls.regular})` }}>
<div className="top">
{this.state.images[random1].user.name}
</div>
<div className="bottom">
{this.state.images[random1].alt_description}
</div>
</div>
<div className="box outer">
<div className="box inner loaded" style={{ backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.5),
rgba(0, 0, 0, 0.5)), url(${this.state.images[random2].urls.regular})` }}>
<div className="top">
{this.state.images[random2].user.name}
</div>
<div className="bottom">
{this.state.images[random2].alt_description}
</div>
</div>
<div className="box inner loaded" style={{ backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.5),
rgba(0, 0, 0, 0.5)), url(${this.state.images[random3].urls.regular})` }}>
<div className="top">
{this.state.images[random3].user.name}
</div>
<div className="bottom">
{this.state.images[random3].alt_description}
</div>
</div>
</div>
<button
onClick={() => {loadImages()}}
className="button">
Load Photos
</button>
</>
);
}
return(
// Before 'Load Photos' button is clicked,
// app will only render skeleton of layout
<>
<div className="box loading">
<div className="top"> </div>
<div className="bottom"> </div>
</div>
<div className="box outer">
<div className="box inner loading">
<div className="top"> </div>
<div className="bottom"> </div>
</div>
<div className="box inner loading">
<div className="top"> </div>
<div className="bottom"> </div>
</div>
</div>
<button
onClick={() => {loadImages()}}
className="button">
Load Photos
</button>
</>
);
};
};
ReactDOM.render(<App />,
document.getElementById("root"))
View Compiled