101

Rediseño de Instagram con Neomorfismo: consume su API para darle animaciones con CSS y JavaScript

9646Puntos

hace un mes

Curso Práctico de Maquetación y Animaciones con CSS
Curso Práctico de Maquetación y Animaciones con CSS

Curso Práctico de Maquetación y Animaciones con CSS

Aprende a maquetar sitios web profesionales con HTML, CSS, JavaScript y animaciones. Crea la estructura, diseño web, animaciones y microinteracciones de tu proyecto con CSS Grid. Accede al DOM de tu aplicación con JavaScript para interactuar con tus usuarios. Pon en práctica tus habilidades como frontend developer con tu profesora Estefany Aguilar.

¡Te doy la bienvenida a un nuevo reto de CSS! Esta vez vamos a hacer el rediseño de Instagram y consumiremos su API para mostrar información real.

Compártenos el resultado final en los comentarios y/o en Twitter con el hashtag #RetosPlatziCSS. 😉

Rediseño Instagram

Antes de comenzar, los cursos de Platzi que te recomiendo para realizar este reto son los siguientes:

Paso 0: planeación

Para este reto te propongo que realices siguientes 5 pasos:

  1. Obtén el token de acceso de usuario de Instagram de corta duración
  2. Usa el token para hacer consultas sobre tu perfil a la API de Instagram
  3. Haz el consumo de la API de Instagram con JavaScript
  4. Embellece tu perfil con neomorfismo
  5. ¡Compártenos tu resultado en los comentarios!

💡 Recomendación: Evita copiar y pegar el código. Trata de transcribir cada detalle y entender muy bien su funcionamiento (sin afán, tómate tu tiempo). Solo de esta forma podrás convertirte en un(a) experto(a) en maquetación con CSS.

ʕ•́ᴥ•̀ʔっ ¡Empecemos!

Paso 1: Obtén el token de acceso de usuario de Instagram de corta duración

Para obtener el token de acceso, Instagram nos pedirá un enlace de nuestro proyecto (en un ratito te muestro para qué). Así que, lo primero que debemos hacer es crear un repositorio en GitHub y hacer el deploy con GitHub Pages. En mi caso, creé un repositorio llamado instagram-redesign-app así:

GitHub Pages

Para hacer el deploy con GitHub Pages, lo primero que debemos hacer es ir a la pestaña de Configuraciones:

GitHub Pages

En Configuraciones, debemos ir a la sección que dice GitHub Pages y darle click al enlace que dice Check it out here! (o, ¡Compruébalo aquí! en español):

GitHub Pages

Aquí debemos cambiar la fuente que dice None por main:

GitHub Pages

Seguido de esto, GitHub nos mostrará el enlace de nuestro sitio que nos servirá para obtener nuestro token y poder acceder a la información de nuestro usuario en Instagram:

GitHub Pages

Luego de tener desplegado el repositorio de nuestro proyecto obtendremos el token de acceso siguiendo los 5 pasos de este tutorial de Facebook for Developers.

Aquí te enseño como me quedó mi configuración final en donde obtengo el client_id (identificador de la app en Instagram), el client_secret (clave secreta de la app de Instagram) y el redirect_uri (URI de redireccionamiento de OAuth válidos):

Facebook for Developers

Con el client_id y el redirect_uri debemos armar la siguiente URL (que encuentras en el paso número 4 del tutorial):

https://api.instagram.com/oauth/authorize?client_id={app-id}&redirect_uri={redirect-uri}&scope=user_profile,user_media&response_type=code

Esta URL la pegas en el navegador. Allí te saldrá una ventanita para autorizar y, apenas autorices, te debe redirigir a una página como esta:

Rediseño de Instagram

Si te surgen inconvenientes en este paso, no dudes en escribirnos en los comentarios 😊

Ahora con el código que te da Instagram al hacer la redirección (como pudiste ver en la imagen) vas a hacer un POST a la API de Instagram (https://api.instagram.com/oauth/access_token) desde Postman, así:

8

Al tener listos los valores de las keys del body client_id, client_secret, grant_type, redirect_uri y code, le vas a dar click en Send e Instagram te dará el access_token y el user_id para poder hacer las peticiones más adelante sobre tu perfil.

Paso 2: Usa el token para hacer consultas sobre tu perfil a la API

Con el token que obtuvimos anteriormente podremos hacer diferentes peticiones a la API de Instagram. En nuestro caso, queremos obtener 2 cosas: la primera, información del usuario (como el username y cantidad de publicaciones); y la segunda, información del contenido multimedia del usuario (como fotos y videos publicados).

Instagram nos provee la siguiente documentación para obtener perfiles de usuario y contenido multimedia de usuario. Lo que debemos hacer nosotros es usar Postman (o el mismo navegador) para realizar ambas peticiones.

Para hacer la petición GET en Postman a la API https://graph.instagram.com/me (para obtener el id y el username del usuario) debemos dar click en donde dice params y agregar dos keys: fiels y access_token, y como valores: id,username y tu_access_token respectivamente. Así:

9

Ahora vamos a hacer la petición al contenido multimedia desde el navegador (también lo puedes hacer desde Postman, pero quiero mostrarte otro camino). Para ello escribimos la URL completa (https://graph.instagram.com/me?fields=id,username,media_count&access_token=tu_access_token) en la barra de búsqueda, damos enter y listo, tendríamos algo como esto:

10

Paso 3: Haz el consumo de la API con JavaScript

Ya hicimos las dos peticiones a la API de Instagram para obtener la información que necesitamos: username y contenido multimedia.

Ahora es momento de hacer estas peticiones en nuestro proyecto con JavaScript y para ello utilizaremos azync/await.
Inicialmente añadiremos estas dos variables:

const BASE_API = 'https://graph.instagram.com/me'const ACCESS_TOKEN = tu_access_token

Luego crearemos dos funciones: una para hacer la petición a la API que nos entrega la información del usuario (username y media_count); y otra para hacer la petición a la API que nos entrega la información multimedia del usuario (id y media_url).

La función que nos provee la información del usuario quedaría de la siguiente forma (puse un console.log para que veas en consola la respuesta):

asyncfunctiongetUserInfo() {
  const response = await fetch(`${BASE_API}?fields=username,media_count&access_token=${ACCESS_TOKEN}`)
  const userInfo = await response.json()
  console.log(userInfo)
  return userInfo
}

getUserInfo()

💡 Para comprender muchísimo mejor las líneas que acabamos de escribir, te recomiendo ver la clase: Conociendo Async/await del Curso de Asincronismo con JavaScript.

La función que nos provee la información multimedia del usuario quedaría de la siguiente forma (puse un console.log para que veas en consola la respuesta):

asyncfunctiongetUserMediaInfo() {
  const response = await fetch(`${BASE_API}/media?fields=id,media_url&access_token=${ACCESS_TOKEN}`)
  const userMediaInfo = await response.json()
  console.log(userMediaInfo)
  return userMediaInfo
}

getUserMediaInfo()

Para poder visualizar esa información, debemos preparar nuestro HTML de la siguiente manera:

<h1id="username"></h1><pid="posts"></p><divid="photos"></div>

Y, desde JavaScript accedemos a él así:

const username = document.getElementById('username')
const posts = document.getElementById('posts')
const photos = document.getElementById('photos')

Para comprender muchísimo mejor las 3 líneas que acabamos de escribir, te recomiendo ver esta clase sobre Cómo acceder al DOM con JavaScript del Curso Práctico de Maquetación y Animaciones con CSS.

A continuación, a nuestra función getUserInfo le añadimos las dos siguientes líneas:

username.innerHTML = userInfo.username
posts.innerHTML = userInfo.media_count

Quedando finalmente de la siguiente manera:

asyncfunctiongetUserInfo() {
  const response = await fetch(`${BASE_API}?fields=username,media_count&access_token=${ACCESS_TOKEN}`)
  const userInfo = await response.json()
  console.log(userInfo)
  username.innerHTML = userInfo.username
  posts.innerHTML = userInfo.media_count
  return userInfo
}

getUserInfo()

Finalmente debemos cambiar la forma en la que ejecutamos nuestra función getUserMediaInfo de la siguiente manera para añadirle un ancho a cada imagen (width), una fuente a cada imagen (src), y las diferentes imágenes al contenedor principal (photos):

getUserMediaInfo().then(media => {
  media.data.map((mediaInfo) => {
    const img = document.createElement('img')
    img.style.width = '100px'img.src = mediaInfo.media_url
    photos.appendChild(img)
  })
})

Así obtendrías un resultado como este:

11

Paso 4: Embellece tu perfil con neomorfismo

"El término Neomorfismo es la combinación de la palabra Nuevo y del término Eskeumorfismo, acuñado por Steve Jobs para describir el aspecto “tradicional” que tenían sus interfaces en las primeras versiones de Iphone. ¡Cómo olvidar estas interfaces!

Siguiendo el segundo principio de la Heurística de Nielsen, esta tendencia trata de imitar de la manera más fiel posible los objetos y artefactos con los que interactuamos en la vida real. Esto se vio reflejado en herramientas como la calculadora, iBook y su vitrina con libros o la app de notas, la que parecía evidentemente un block de anotaciones." Neomorfismo: la nueva tendencia en diseño UI.

Hablando de tendencias: Crea tu planeador semanal con CSS Grid, Flexbox y Glassmorphism

Buscando en Behance diseños neomórficos me encontré con este rediseño de Instagram por Parisa Behboodi el cuál usaremos como guía para embellecer las peticiones que hicimos en el paso anterior.

12

Pero, antes de comenzar, para obtener un resultado neomórfico con HTML y CSS debemos jugar con sombras y con luz así como nos indica este tutorial (aunque también podemos obtener este efecto usando una herramienta online como Neumorphism.io y añadirlo a nuestro código).

Teniendo en cuenta lo anterior, nuestro HTML quedaría así:

<div class="neumorphism"></div>

nuestro CSS así:

:root {
 --gray: #EAEBF3;
}

body {
 background: var(--gray);
 display: grid;
 place-items: center;
}

.neumorphism {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  margin: 20px;
  box-shadow: 9px9px16pxrgb(163 177 198 / 60%),
              -9px -9px16pxrgb(255 255 255 / 50%);
}

Y el resultado final:

13

También puedes verlo en CodePen.

Teniendo ya listo el efecto neomorfico con HTML y CSS podemos comenzar con la maquetación de nuestro perfil de Instagram. Para ello te propongo dividir toda la pantalla en pequeñas partes: Header, Posts, Highlights y Photos.

14

Teniendo en cuenta estas divisiones, podemos comenzar a maquetar sección por sección.

💡 Por cierto, te recomiendo icons8 para los íconos y Google Fonts para la tipografía.

Header

Este Header en particular puede maquetarse de varias formas. Para este caso, te propongo que usemos CSS Grid para crear 5 columnas y ubicar cada uno de los elementos allí así como te muestro en la siguiente imagen:

15

Teniendo en cuenta esta estructura, analicemos cómo podríamos abordar los elementos 1, 2 y 3 desde CSS.

El elemento 1 necesitaría:

16

El elemento 2 necesitaría:

17

Y el elemento 3 necesitaría:

18

Nuestros estilos generales quedarían de la siguiente forma:

@import url('https://fonts.googleapis.com/css2?family=Source+Sans+3:[email protected]&display=swap');

:root {
  --gray: #EAEBF3;
  --dark-gray: #A5A5A5;
  --black: #000000;
  --text-sm: 10px;
  --text-md: 12px;
  --text-lg: 14px;
}

body {
  background: var(--gray);
  font-family: 'Source Sans 3', sans-serif;
}

.text-sm {
  font-size: var(--text-sm);
  text-align: center;
  margin: 0;
}

.text-md {
  font-size: var(--text-md);
  text-align: center;
  margin: 0;
}

.text-lg {
  font-size: var(--text-lg);
  text-align: center;
  margin: 0;
}

.relative {
  position: relative;
}

.center {
  display: grid;
  place-items: center;
}

.icon-sm {
  width: 16px;
}

.icon-md {
  width: 24px;
}

El HTML nos quedaría así:

<header class="header-container">
  <img class="icon-sm" src="https://img.icons8.com/external-becris-lineal-becris/50/000000/external-edit-mintab-for-ios-becris-lineal-becris.png"/>

  <div class="icon-neumorphism center relative">
    <div class="notification"></div>
    <svg class="icon-sm" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"width="30"height="30"
        viewBox="0 0 172 172"
        style=" fill:#000000;"><g fill="none"fill-rule="nonzero"stroke="none"stroke-width="1"stroke-linecap="butt"stroke-linejoin="miter"stroke-miterlimit="10"stroke-dasharray=""stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none"text-anchor="none" style="mix-blend-mode: normal"><path d="M0,172v-172h172v172z"fill="none"></path><g fill="#a5a5a5"><path d="M28.66667,28.66667c-6.33533,0 -11.46667,5.13133 -11.46667,11.46667v86c0,6.33533 5.13133,11.46667 11.46667,11.46667h22.93333v22.93333c0,3.16643 2.5669,5.73333 5.73333,5.73333c1.9417,-0.00362 3.74963,-0.98976 4.80391,-2.62031l19.56276,-26.04636h61.63333c6.33533,0 11.46667,-5.13133 11.46667,-11.46667v-86c0,-6.33533 -5.13133,-11.46667 -11.46667,-11.46667z"></path></g></g></svg>
  </div>

  <div class="center">
    <div class="profile-pic-neumorphism center">
      <div class="story center">
        <img class="profile-picture" src="https://scontent-bog1-1.cdninstagram.com/v/t51.2885-15/18300095_278312585963307_9179315967434424320_n.jpg?_nc_cat=106&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=Zio5O-jBLXkAX9Cx181&_nc_ht=scontent-bog1-1.cdninstagram.com&edm=ANo9K5cEAAAA&oh=81d971eb6ea000a5465982e3d27741d0&oe=61B8D7A3" alt="Mike">
      </div>
    </div>
    <h1 id="username"class="text-lg"></h1>
  </div>

  <div class="icon-neumorphism center">
    <svg class="icon-sm" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"width="30"height="30"
        viewBox="0 0 172 172"
        style=" fill:#000000;"><g fill="none"fill-rule="nonzero"stroke="none"stroke-width="1"stroke-linecap="butt"stroke-linejoin="miter"stroke-miterlimit="10"stroke-dasharray=""stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none"text-anchor="none" style="mix-blend-mode: normal"><path d="M0,172v-172h172v172z"fill="none"></path><g fill="#a5a5a5"><path d="M54.72422,22.93333c-20.726,0 -37.52422,16.79822 -37.52422,37.52422c0,36.07642 41.84746,70.54796 61.39817,85.89922c0.01862,0.01499 0.03728,0.02992 0.05599,0.04479c0.13396,0.10517 0.31608,0.25498 0.44792,0.35833l0.0112,-0.0112c1.98358,1.4988 4.40056,2.31232 6.88672,2.31797c2.48999,-0.00322 4.91127,-0.81686 6.89792,-2.31797v0.0112c0.03859,-0.03025 0.09561,-0.07038 0.13437,-0.10078c0.02948,-0.02312 0.0712,-0.05517 0.10079,-0.07839c0.03755,-0.03335 0.07487,-0.06694 0.11198,-0.10078c19.49011,-15.2961 61.55495,-49.85989 61.55495,-86.02239c0,-20.726 -16.79822,-37.52422 -37.52422,-37.52422c-19.80867,0 -31.27578,17.2 -31.27578,17.2c0,0 -11.46711,-17.2 -31.27578,-17.2z"></path></g></g></svg>
  </div>

  <img class="icon-sm"class="icon" src="https://img.icons8.com/ios/50/000000/settings--v1.png"/>
</header>

Y el CSS nos quedaría así:

.header-container {
  display: grid;
  grid-template-columns: 16px50px150px50px16px;
  margin-top: 20px;
}

.notification {
  position: absolute;
  width: 8px;
  height: 8px;
  background: red;
  border-radius: 50%;
  top: 2px;
  right: 2px;
}

.profile-pic-neumorphism {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  margin: 16px0;
  box-shadow: 9px9px16pxrgb(163 177 198 / 60%),
             -9px -9px16pxrgb(255 255 255 / 50%);
}

.icon-neumorphism {
  align-self: end;
  width: 35px;
  height: 35px;
  border-radius: 50%;
  margin-bottom: 20px;
  justify-self: center;
  box-shadow: 9px9px16pxrgb(163 177 198 / 60%),
             -9px -9px16pxrgb(255 255 255 / 50%);
}

.story {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  background: linear-gradient(to right, red, purple);
}

.profile-picture {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  border: 3px solid var(--gray);
  background: var(--gray);
}

Obteniendo un resultado como el siguiente:

19

Posts

Analicemos lo que necesitamos para esta sección de Posts:

20

Para ello, nuestro HTML nos quedaría así:

<sectionclass="posts-container"><pclass="text-box text-md"><spanid="posts"></span><span>Posts</span></p><pclass="text-box text-md"><span>14</span><span>Followers</span></p><pclass="text-box text-md"><span>83</span><span>Following</span></p></section>

Y el CSS nos quedaría así:

.posts-container {
  display: flex;
  justify-content: space-evenly;
  align-items: center;
  width: 282px;
  height: 45px;
  border-radius: 8px;
  box-shadow: 9px9px16pxrgb(163 177 198 / 60%),
            -9px -9px16pxrgb(255 255 255 / 50%);
  margin-top: 16px;
}

.text-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0;
}

.text-boxspan + span {
  color: var(--dark-gray);
}

Obteniendo un resultado como el siguiente:

21

Highlights

Analicemos lo que necesitamos para esta sección de Highlights:

22

Para ello, nuestro HTML nos quedaría así:

<ulclass="highlights-container"><li><divclass="center"><imgclass="icon-sm"src="https://img.icons8.com/external-kmg-design-outline-color-kmg-design/32/000000/external-shopping-bag-e-commerce-kmg-design-outline-color-kmg-design-1.png"/></div><pclass="text-sm">Shopping</p></li><li><divclass="center"><imgclass="icon-sm"src="https://img.icons8.com/external-vitaliy-gorbachev-lineal-color-vitaly-gorbachev/60/000000/external-carrot-vegetable-vitaliy-gorbachev-lineal-color-vitaly-gorbachev.png"/></div><pclass="text-sm">Life Style</p></li><li><divclass="center"><imgclass="icon-sm"src="https://img.icons8.com/external-filled-outline-icons-pause-08/64/000000/external-clothes-autumn-filled-outline-icons-pause-08.png"/></div><pclass="text-sm">Trend ?!</p></li><li><divclass="center"><imgclass="icon-sm"src="https://img.icons8.com/external-vitaliy-gorbachev-lineal-color-vitaly-gorbachev/60/000000/external-plane-summer-vitaliy-gorbachev-lineal-color-vitaly-gorbachev.png"/></div><pclass="text-sm">Travel</p></li><li><divclass="center"><imgclass="icon-sm"src="https://img.icons8.com/external-bearicons-outline-color-bearicons/64/000000/external-Dog-chinese-new-year-bearicons-outline-color-bearicons.png"/></div><pclass="text-sm">My Dog</p></li></ul>

Y el CSS nos quedaría así:

.highlights-container {
  list-style-type: none;
  display: flex;
  padding: 0;
}

.highlights-containerdiv {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  box-shadow: 9px9px16pxrgb(163 177 198 / 60%),
            -9px -9px16pxrgb(255 255 255 / 50%);
  margin: 8px;
}

Obteniendo un resultado como el siguiente:

23

Photos

Analicemos lo que necesitamos para esta sección de Photos:

24

Para ello, nuestro HTML nos quedaría así:

<section id="photos"class="photos-container"></section>

Y el CSS nos quedaría así:

.photos-container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 4px;
  width: 282px;
}

.photos-containerimg {
  border-radius: 8px;
  width: 90px;
  height: 90px;
  object-fit: cover;
}

Obteniendo un resultado como el siguiente:

25

Paso 5: ¡Compártenos tu resultado en los comentarios!

¡Este paso es mi favorito! Compártenos en los comentarios y/o en Twitter con el hashtag #RetosPlatziCSS el resultado final del rediseño de tu Perfil de Instagram. 😄

Aquí te comparto mi codepen y también mi repositorio de GitHub con el resultado final.

0

Además, cuéntanos qué otro tipo de retos te gustaría encontrar en esta sección.

#NuncaParesDeAprender

Curso Práctico de Maquetación y Animaciones con CSS
Curso Práctico de Maquetación y Animaciones con CSS

Curso Práctico de Maquetación y Animaciones con CSS

Aprende a maquetar sitios web profesionales con HTML, CSS, JavaScript y animaciones. Crea la estructura, diseño web, animaciones y microinteracciones de tu proyecto con CSS Grid. Accede al DOM de tu aplicación con JavaScript para interactuar con tus usuarios. Pon en práctica tus habilidades como frontend developer con tu profesora Estefany Aguilar.
Estefany
Estefany
teffcode

9646Puntos

hace un mes

Todas sus entradas
Escribe tu comentario
+ 2
Ordenar por:
3
2204Puntos

Qué reto tan increible!

2
9646Puntos
un mes

Gracias, Jean !!! ✨🚀

3
8146Puntos

Que gran reto, la mejor manera de aprender son estos tipos de practicas.

2
9646Puntos
un mes

Graciasss… Así es, Iván !!! ✨🚀

3
839Puntos

Tremendo tutorial, muy detallado!! Lo voy a poner en práctica. Tesa!!

2
9646Puntos
un mes

Gracias, Juancho !!! 🥰 Feliz de ver tu resultadooo ✨

3
8995Puntos

Este tutorial es la excelencia pura!

2
9646Puntos
un mes

Es con todo el amor !!! 💚

2
74Puntos

Yo tengo un problema 😦.

Al realizar la petición post con la info del cient_id etc, la petición retorna un bad request mencionando lo siguiente:

{
    "error_type": "OAuthException",
    "code": 400,
    "error_message": "This grant type is not supported"
}
2
9646Puntos
un mes

Me mandas pantallacito con la petición que estás haciendo para revisarlo ? 😊

2
3577Puntos

¡Te amo, teff!

1
9646Puntos
un mes

Y yo más a ustedes !!! 😊 💚

2
7954Puntos

Está buenísimo!
Muchas gracias por compartir! 😄

1
17774Puntos

Listo mi resultado.
Captura de Pantalla 2021-12-24 a la(s) 14.04.09.png

Una pequeña corrección que quisiera sugerir en el código de la función “getUserMediaInfo” para que las imágenes obtengan el ancho adecuado:

getUserMediaInfo().then(media => {
  media.data.map((mediaInfo) => {
    const img = document.createElement('img')
    // img.style.width = "90px" // Simplemente no usemos esta línea :-)
    img.src = mediaInfo.media_url
    photos.appendChild(img)
  })
})```