O projeto é um clone do site HBO Max, com o intuito de reproduzir a interface, com algumas modificações, aplicando os temas abordados ao longo das aulas de CSS da plataforma da Digital Innovation One.
Eu utilizando como base o projeto da dio.me proposto pela Instrutora: Michele Ambrosio, implentei uma Single Page Aplication, utilizando-se de várias técnologias adicionais, como por exemplo a utilização de componentes nativos do JavaScript, também convenções de nomenclatura e estruturação.
- Menu de navegação
- Cabeçalho com animação gradiente
- Cards com os planos de assinatura animados
- Lista de filmes e séries disponíveis na plataforma
- Formulário de login
- Rodapé com links importantes
- UI Responsiva
As features são visuais, não possuindo integração com nenhuma API. O intuito do projeto é reproduzir a interface do site original, com algumas modificações.
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
- Transformações 2D e 3D
- Transições e animações
- Tratamento de campos inválidos no formulário
Melhorias na:
- Responsividade
- Estruturação
- nomenclaturas
Implementações:
- ViteJs
- TypeScript
- Web Components
- SASS
- SCSS
- SPA
- Página de não econtrada 404
Convenções 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 variável counter para definir a posição específica 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, não redireciona para 404
match = { route: { path: '', view: HomeView }, isMatch: true }; // Usa uma view padrão
} 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}" não encontrado.`);
}
}
init();
};
window.addEventListener('hashchange', router); // Escuta mudanças no hash
document.addEventListener('DOMContentLoaded', (): void => {
// Defina o hash padrão se estiver vazio
if (!window.location.hash) window.location.hash = "#/"; // Defina o hash inicial como a página 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 padrão
const currentUrl: string = window.location.hash; // Obtém o hash atual
const linkUrl: string = (target as HTMLAnchorElement).href.split('#')[1]; // Obtém o hash do link
// Navega para o novo hash
if (`#${linkUrl}` !== currentUrl) navigateTo(linkUrl);
}
});
router(); // Chama o router na inicialização
});
Você pode acessar ao resultado final do projeto clicando aqui.
Jose Alves
Instagram | GitHub | LinkedIn
✔️ Por Jose Alves 🫡