hbomax

Projeto final da formação de CSS proposto pela dio.me, aproveitando o layout fiz a minha própria aplicação transformando-a em uma SPA (Router própio sem frameworks), utilizando web components, convenções de pastas e nomenclatura ITCSS e BEMIT, e usufluindo dos recursos do sass criando mixins.

Stars
0
Committers
2

Sumrio

  • Menu de navegao
  • Cabealho com animao gradiente
  • Cards com os planos de assinatura animados
  • Lista de filmes e sries disponveis na plataforma
  • Formulrio de login
  • Rodap com links importantes
  • UI Responsiva

As features so visuais, no possuindo integrao com nenhuma API. O intuito do projeto reproduzir a interface do site original, com algumas modificaes.

O projeto possui como intuito aplicar os conceitos abordados na Trilha de CSS da DIO, ministrada pela instrutora Michele Ambrosio.

Recursos CSS presentes no projeto:

  • Fundamentos do CSS
  • Grid Layout
  • Flexbox
  • Responsividade
  • Pseudo-elementos
  • Pseudo-classes
  • Transformaes 2D e 3D
  • Transies e animaes
  • Tratamento de campos invlidos no formulrio

Melhorias na:

  • Responsividade
  • Estruturao
  • nomenclaturas

Implementaes:

  • ViteJs
  • TypeScript
  • Web Components
  • SASS
  • SCSS
  • SPA
  • Pgina de no econtrada 404

Convenes utilizadas:

  • ITCSS
  • BEMIT

Cria uma lista de URL's, com o @each itera item por item, e por fim foi criadas seis classes, utilizando a varivel counter para definir a posio especfica de cada card.

$urls: (
  '/images/hbo-hover_0.webp',
  '/images/MAX-Hover.webp',
  '/images/DC-Hover.webp',
  '/images/WB-Hover.webp',
  '/images/CN-Hover.png',
  '/images/UCL-Hover.webp'
);

@mixin generate-hover-backgrounds($urls) {
  $counter: 1;

  @each $url in $urls {
    .c-contents__card:nth-child(#{$counter}):hover {
      cursor: pointer;
      background-image: url(#{$url});
    }

    $counter: $counter + 1;
  }
}

Router desenvolvido utilizando recursos nativos do JavaScript e do navegador.

// Views
import HomeView from './views/home.view';
import NotFoundView from './views/not-found.view';
import SignInView from "./views/signin.view";

// Interfaces
import IRoute from "./interfaces/IRoute.interface";
import IPotentialMatch from "./interfaces/IPotentialMatch.interface";

// Utils
import getElement from './utils/get-element.util';
import setClasses from './utils/set-classes.util';
import sleep from './utils/sleep.util';

const navigateTo = (url: string): void => {
  window.location.hash = url; // Muda o hash da URL
  router();
};

const router: () => Promise<void> = async (): Promise<void> => {
  const routes: IRoute[] = [
    { path: "#/", view: HomeView },
    { path: "#/signin", view: SignInView },
    { path: '#/404', view: NotFoundView }
  ];

  // Test each route potential match
  const potentialMatches: IPotentialMatch[] = routes.map((route: IRoute): IPotentialMatch => {
    return {
      route: route,
      isMatch: location.hash === route.path // Verifica o hash
    }
  });

  let match: IPotentialMatch | undefined = potentialMatches
    .find((potentialMatch: IPotentialMatch): boolean => potentialMatch.isMatch);

  if (!match) {
    // Verifica se o hash corresponde a um ID existente
    const hashId = location.hash.substring(1);
    const element = document.getElementById(hashId);
    
    if (element) {
      // Se o ID existir, no redireciona para 404
      match = { route: { path: '', view: HomeView }, isMatch: true }; // Usa uma view padro
    } else {
      match = {
        route: routes.find((route: IRoute): boolean => route.path === '#/404') || routes[routes.length - 1],
        isMatch: true
      };
    }
  }

  const root: HTMLElement = getElement('#root');
  const view = new match.route.view();

  const init: () => Promise<void> = async (): Promise<void> => {
    root.innerHTML = await view.getHtml();
    setClasses(['main'], true, 'is-rendered');

    const isRendered: boolean 
      = getElement('main').classList.contains('is-rendered');

    if (isRendered) {
      getElement('#loader').classList.add('is-hidden');
      setClasses(['max-navbar', 'max-footer'], true, 'is-visible');
      await sleep(.3);
      setClasses(['#root'], false, 'is-show');
    }

    // Rolagem para o ID especificado no hash
    const hash: string = window.location.hash.substring(1); // Remove o '#'
    
    if (hash) {
      const element: HTMLElement | null = document.getElementById(hash);

      if (element) element.scrollIntoView({ behavior: 'smooth' }); // Rola suavemente para o elemento
      else console.warn(`Elemento com ID "${hash}" no encontrado.`);
    }
  }

  init();
};

window.addEventListener('hashchange', router); // Escuta mudanas no hash

document.addEventListener('DOMContentLoaded', (): void => {
  // Defina o hash padro se estiver vazio
  if (!window.location.hash) window.location.hash = "#/"; // Defina o hash inicial como a pgina inicial

  document.body.addEventListener('click', (e: MouseEvent): void => {
    const target = e.target as HTMLElement;

    if (target.matches('[data-link]')) {
      e.preventDefault(); // Sempre prevenir o comportamento padro
      const currentUrl: string = window.location.hash; // Obtm o hash atual
      const linkUrl: string = (target as HTMLAnchorElement).href.split('#')[1]; // Obtm o hash do link
      
      // Navega para o novo hash
      if (`#${linkUrl}` !== currentUrl) navigateTo(linkUrl); 
    }
  });

  router(); // Chama o router na inicializao
});

Inspirao

Voc pode acessar ao resultado final do projeto clicando aqui.


Por Jose Alves