15 minutes

Site Web D'entreprise avec React, Styled Components et Typescript

Dernière mise à jour: 1 juin, 2022

Vous pourrez suivre si vous avez un minimum de connaissances en matière de reactjs et de typescript. Nous allons utiliser intersection observer et ajouter une carte fonctionnelle avec mapbox-gl. Vous pouvez utiliser ce site Web comme modèle pour vos futurs projets et l'ajouter à votre portfolio personnel. Commençons!

Vous pouvez trouver le site Web fini ici.

Vous pouvez trouver le code source de ce projet ici. Si vous avez l'impression d'être perdu ou s'il y a une erreur bizarre que vous ne pouvez pas corriger, n'hésitez pas à consulter ce référentiel.

§Résumé du Projet

Ce site Web est un projet de landing page pour une entreprise. Dans ce projet particulier, je crée un exemple de société de marketing. Cependant, je pense que ce site Web peut convenir à de nombreuses autres entreprises avec quelques modifications minimes. You can see that we included some forms for clients (or potential clients) to contact us but since we don't have backend for this porject we will only log these informations.

Nous montrerons aux visiteurs les projets que nous avons réalisés, nos partenaires, les travaux récents, les articles de blog et enfin nos partenaires (inventés). Nous permettrons aux utilisateurs de nous suivre sur les réseaux sociaux et de s'abonner à notre newsletter hebdomadaire.

§Configuration du Projet

Nous pouvons simplement initialiser nos projets react avec du typescript avec cette commande :

npx create-react-app corporate-react-ts --template typescript

Lorsque l'installation est terminée, je supprime généralement tout ce qui n'est pas nécessaire dans le fichier App.tsx. Allez-y et supprimez tout le code de démarrage dans notre App.tsx et commencez à écrire le code nécessaire à notre projet. Nous utiliserons de styled composants, de cette façon nous pouvons conserver facilement le CSS nécessaire au composant à l'intérieur du composant et nous n'avons pas besoin de créer de fichiers CSS.

import React from 'react'; import styled from 'styled-components'; function App() { return ( <AppWrapper> App file </AppWrapper> ); } export default App; const AppWrapper = styled.div` min-height: 100vh; display: flex width: 100vw; `;

Nous pouvons créer un fichier CSS global à l'aide de styled components, mais dans ce projet, je ne pensais pas que ce serait nécessaire, nous conservons donc toujours le fichier index.css et avec quelques modifications de base. Nous ajouterons également des couleurs qui, je pense, correspondent à nos sites Web. Même si je n'utilise pas toutes les couleurs dans mes projets, j'aime avoir les couleurs qui vont bien ensemble dans mon projet pour des améliorations et des ajouts futurs. Vous pouvez trouver le code complet nécessaire jusqu'à présent pour index.css ici :

html { scroll-behavior: smooth; } * { padding:0; margin:0; box-sizing: border-box; } :root { --white: rgb(255, 255, 255); --black: rgb(0,0,0); --gray: rgb(69, 69, 69); --gray-2: rgb(149, 149, 149); --yellow: #ffbe0b; --pink-lavender: #cdb4dbff; --orchid-pink: #ffc8ddff; --nadeshiko-pink: #ffafccff; --uranian-blue: #bde0feff; --darker-blue: rgb(63, 108, 151); --baby-blue-eyes: #a2d2ffff; --top-margin: 100px; } body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; }

Nous utiliserons des images pour rendre notre projet aussi réel que possible. Vous pouvez bien sûr utiliser vos propres images ou visiter l'un des nombreux sites Web qui fournissent des visuels gratuits et utiliser certains des leurs. J'ai rassemblé la plupart des visuels utilisés dans ce projet en visitant des sites Web tels que unsplash, pexels etc. J'ai redimensionné et changé le format de mes visuels pour les garder plus légers. Ceci est également suggéré et peut être géré gratuitement en ligne. Pour enregistrer vos visuels dans le dossier src, créez un dossier appelé assets. Enregistrez vos visuels ici.

Puisque nous créons une seule page, nous n'avons pas besoin de gérer le routage pour ce projet. J'aime commencer à créer de haut en bas. Nous devons donc tout d'abord créer la barre de navigation (navbar), puis les sections et enfin le pied de page (footer) de notre site Web.

§Gardez un Padding Cohérent avec Un Component Wrapper

Les bibliothèques CSS telles que tailwindcss et bootstrap offrent des solutions d'espacement et de remplissage dans différentes tailles d'écran. Ne pas avoir à le faire manuellement est un gain de temps, mais nous utilisons des styled composants dans ce projet particulier. Nous devons donc créer un composant pour envelopper d'autres composants que nous voulons avoir le même padding. Comme nous allons utiliser ce composant dans d'autres composants, il est préférable de créer ce composant dans le dossier components.

Créez un fichier appelé ContainerComponent.tsx dans le dossier components. Ce composant définira la valeur display de flex et la direction sera décidée avec le prop que nous lui avons défini. Encore une fois, nous devons définir un type pour le prop. Grâce aux styled composants, nous pouvons définir la valeur de flex-direction: column si nécessaire. Le paramètre de padding sera assez basique, plus la valeur de padding de l'écran sera petite. J'aime aussi garder une largeur maximale (max-width), donc dans les grands écrans, notre page Web gardera ce look simple et propre. Le code complet de ce fichier est ici :

import React, { ReactNode } from 'react' import styled from 'styled-components' interface PropType { children: ReactNode column: boolean } const ContainerComponent = (props:PropType ) => { return ( <Container column={props.column}> {props.children} </Container> ) } export default ContainerComponent; const Container = styled.div<PropType>` display:flex; flex-direction: ${props => props.column ? "column": "row"}; width: 100%; padding-left: 10px; padding-right: 10px; max-width: 1920px; @media (max-width: 1920px) { padding-left: 80px; padding-right: 80px; } @media (max-width: 1440px) { padding-left: 60px; padding-right: 60px; } @media (max-width: 1200px) { padding-left: 50px; padding-right: 50px; } @media (max-width: 960px) { padding-left: 40px; padding-right: 40px; } @media (max-width: 600px) { padding-left: 30px; padding-right: 30px; } @media (max-width: 480px) { padding-left: 20px; padding-right: 20px; } `

C'est tout maintenant notre contenu semblera aligné quelle que soit la taille de l'écran.

§Création de Notre Barre de Navigation React

Créez un dossier appelé components dans le dossier src. Nous garderons nos composants ici. Dans ce dossier, créez un dossier navbar. Dans la barre de navigation, nous aurons besoin du logo de notre entreprise. Nous utiliserons également ce logo comme bouton d'accueil qui nous amènera en haut de la page. Il existe de nombreux sites Web qui vous permettent de concevoir des logos gratuitement ou, si vous préférez, vous pouvez également utiliser une image. J'ai transformé mon logo en composant et voici le simple composant Logo.tsx que j'ai créé à l'intérieur de la navbar :

import React from 'react' import styled from 'styled-components'; import logo from '../../assets/marketing-co.png'; const Logo = () => { return ( <a href='/#'> <Img src={logo} alt="brand logo" /> </a> ) } export default Logo const Img = styled.img` border-radius: 50%; margin: 5px; width: 94px; `

Nous aurons également besoin de liens vers différentes sections de notre page Web. Nous transformerons également ces liens en composants pour garder mon code aussi propre que possible. Nous appellerons ce composant NavbarLink.tsx. Ces composants prendront une chaîne comme accessoire afin que nous puissions envoyer nos visiteurs où ils veulent aller sur notre page Web. Nous devons également remplacer les espaces par "-" bien sûr. En dehors de cela, c'est juste un composant simple comme vous pouvez le voir ici :

import React from 'react' import styled from 'styled-components' interface PropType { to: string; } const NavbarLink = ({ to }: PropType) => { const link = to.replace(' ', '-') return ( <A href={`#${link}`}>{to}</A> ) } export default NavbarLink const A = styled.a` text-decoration: none; color: var(--baby-blue-eyes); font-size: 1.6rem; margin: 10px; text-transform: uppercase; transition: 0.4s; :hover { color: var(--nadeshiko-pink); } `

Maintenant que la barre de navigation de base pour les écrans plus grands est terminée, nous pouvons penser à des tailles d'écran plus petites. Dans la plupart de mes projets, je pense d'abord au bureau (desktop), contrairement à ce qui est suggéré. Quoi qu'il en soit, nous avons besoin d'une barre latérale au lieu d'une grande barre de navigation en haut de l'écran et nous avons besoin d'un bouton hamburger pour basculer cette barre latérale pour la navigation.

Je pense qu'une explication fonctionnera mieux après avoir vu ces composants fonctionner ensemble. Créons maintenant un composant appelé HamburgerComponent.tsx dans le dossier navbar. Ce composant basculera la barre latérale que nous allons créer. Le code utilisé dans ce composant est ici :

import React, { useState } from 'react' import styled from 'styled-components' import RightNav from './RightNav' const Hamburger = () => { const [open, setOpen] = useState<boolean>(false) return (<> <StyledHamburger open={open} onClick={() => {setOpen(!open)}}> <div></div> <div></div> <div></div> </StyledHamburger> <RightNav open={open} /> </>) } export default Hamburger; const StyledHamburger = styled.div<{ open: boolean }>` position: ${({ open }) => open ? "fixed" : 'absolute'}; top: 20px; right: 50px; width: 40px; z-index: 20; height: 40px; display: flex; flex-direction: column; justify-content: space-around; align-items: center; &:hover { cursor: pointer; } @media screen and (min-width: 1201px) { display: none; } div { width: 40px; height: 0.25rem; background-color: ${({ open }) => open ? "var(--nadeshiko-pink)" : 'var(--baby-blue-eyes)'}; border-radius: 10px; transform-origin: 1px; transition: all 0.3s ease-in-out; &:nth-child(1) { transform: ${({ open }) => open ? 'rotate(45deg)' : 'rotate(0)'}; } &:nth-child(2) { transform: ${({ open }) => open ? 'translateX(100%)' : 'translateX(0)'}; opacity: ${({ open }) => open ? 0 : 1}; } &:nth-child(3) { transform: ${({ open }) => open ? 'rotate(-45deg)' : 'rotate(0)'}; } } `

Comme vous pouvez le voir, ce composant ne sera visible que lorsque la taille de l'écran est inférieure à 1200 pixels. Nous transmettons également la valeur booléenne que nous avons définie à notre composant RightNav. Nous pouvons maintenant créer notre fichier RightNav.tsx dans le dossier navbar. J'ai défini le type de la valeur pour RightBarWrapper comme suit. Je voulais que cela reste ainsi pour montrer que nous pouvons également définir les types séparément de cette façon. Le code est très simple ici, comme vous pouvez le voir :

import React from 'react' import styled from 'styled-components' import RightNavbarLink from './RightNavbarLink'; interface propType { open:boolean } const RightNav = ( props: propType) => { return ( <RightBarWrapper open={props.open}> <LinkContainer> <RightNavbarLink to="about" /> <RightNavbarLink to="we offer" /> <RightNavbarLink to="portfolio" /> <RightNavbarLink to="recent work" /> <RightNavbarLink to="contact" /> </LinkContainer> </RightBarWrapper> ) } export default RightNav const RightBarWrapper = styled.div<{open:boolean}>` position: fixed; display: flex; border-left: 4px solid var(--baby-blue-eyes); justify-items: center; transition: all 0.3s ease-in-out; background: var(--white); transform: ${({ open }) => open ? 'translateX(0)' : 'translateX(100%)'}; top: 0; right: 0; height: 100vh; width: 250px; z-index:10; @media screen and (min-width: 1201px) { display: none; } ` const LinkContainer = styled.div` margin-top: 100px; display: flex; flex-direction: column; width: 100%; margin-left: 30px; `

Ici, nous avons un composant appelé RightNavbarLink que nous n'avons pas encore. Alors allons-y et créons. Ce sera très similaire au composant que nous avons créé pour nos liens. Créez le fichier RightNavbarLink.tsx dans le dossier navbar. Ici vous pouvez voir le code complet de ce composant :

import React from 'react' import styled from 'styled-components'; interface propType { to: string; } const RightNavbarLink = ({ to }: propType) => { const link = to.replace(' ', '-') return ( <A href={`#${link}`}>{to}</A> ) } export default RightNavbarLink const A = styled.a` transition: 0.2s; color: var(--baby-blue-eyes); text-decoration: none; text-transform: uppercase; transform: 1s; margin-bottom: 20px; font-size: 1.4rem; :hover { color: var(--nadeshiko-pink); } `

Maintenant que nous avons toutes les pièces nécessaires pour notre barre de navigation, nous pouvons créer cette barre. Dans le dossier navbar, nous devons créer le fichier Navbar.tsx pour tout. Notre barre de navigation changera de couleur d'arrière-plan lorsque le composant de la bannière (banner) n'est pas visible. Cette fonctionnalité sera ajoutée ultérieurement lorsque nous créerons notre composant Banner. Nous pourrons le gérer facilement avec l'aide de intersection observer.

Nous allons définir la valeur de hauteur de ce composant sur une valeur de notre choix. Il est recommandé de conserver cette valeur comme variable. De cette façon, si nous devons modifier la valeur de hauteur, la mise en page ne se cassera pas et ne semblera pas étrange. Notre barre de navigation aura "position:fixed", de cette façon nous la verrons où que nous soyons sur la page pour une navigation facile. Définir également le "z-index:100" garantira que notre composant apparaît au-dessus de tous les autres.

Le code de ce fichier devrait ressembler à ceci :

import React from 'react' import styled from 'styled-components' import ContainerComponent from '../ContainerComponent' import Hamburger from './HamburgerComponent' import Logo from './Logo' import NavbarLink from './NavbarLink' const Navbar = (props: { bgColor: string }) => { return ( <Nav bgColor={props.bgColor} id="navbar"> <ContainerComponent column={false}> <Logo /> <RightAlign> <NavbarLink to="about" /> <NavbarLink to="we offer" /> <NavbarLink to="portfolio" /> <NavbarLink to="recent work" /> <NavbarLink to="contact" /> </RightAlign> <Hamburger /> </ContainerComponent> </Nav> ) } export default Navbar const Nav = styled.nav<{ bgColor: string }>` background: ${props => props.bgColor}; height: var(--top-margin); position: fixed; transition: .4s; transition-delay: 1s; width: 100%; max-width: 100vw; display: flex; flex-direction: row; z-index: 100; ` const RightAlign = styled.div` margin-left: auto; display: flex; flex-direction: row; align-items: center; @media (max-width: 1200px) { display: none; } `

§Créons Une Bannière Cool

La bannière est très importante pour les landing pages. Vous avez besoin d'une bannière propre avec votre argumentaire ou votre slogan.Étant donné que ce projet est un modèle, nous allons simplement l'imiter avec un simple slogan et du texte lorem ipsum. L'utilisation d'une image en arrière-plan vous coûtera certainement en termes de performances, mais cela peut être bénéfique d'un point de vue marketing. Pour ce projet, j'ai utilisé une image, bien sûr après avoir redimensionné et changé le format en webp.

La partie délicate de ce composant est l'intersection observer. Heureusement, il existe un package npm que nous pouvons utiliser à cette fin. Installez le package react-intersection-observer avec la commande suivante ci-dessous :

npm i react-intersection-observer

Nous aurons également besoin d'un bouton pour que les visiteurs puissent facilement défiler jusqu'à la section suivante. Nous pourrions utiliser ce bouton dans d'autres parties de notre site Web, il serait donc préférable de créer un composant que nous pouvons importer. De cette façon, nous pourrons conserver un style cohérent sur l'ensemble de nos sites Web. Dans notre dossier components, créons le composant Button.tsx. Le bouton devrait bien sûr prendre un accessoire de texte, mais à ce stade, nous ne savons pas encore si nous voulons ajouter la fonctionnalité "onClick". Le code de ce composant :

import React from 'react' import styled from 'styled-components'; interface propType { text: string, onClick?: Function, } const Button = (prop: propType) => { return ( <StyledButton> {prop.text} </StyledButton> ) } export default Button const StyledButton = styled.button` padding: 14px 20px; font-size: 1.4rem; border-radius: 30px; border: 2px solid white; color: var(--white); background: linear-gradient(25deg, var(--uranian-blue), var(--nadeshiko-pink)); text-transform: capitalize; transition: .4s; :hover { border: 2px solid black; background: inherit; cursor: pointer; color: var(--black); } `

Nous voulons tout conserver dans le composant de bannière au centre sous forme de colonne. Puisque nous avons déjà créé notre composant de conteneur, nous pouvons simplement passer la valeur de la colonne en tant que "true", ce sera un jeu d'enfant. App.tsx sera le composant parent que nous allons créer ici. Nous transmettrons au setter notre bannière lorsque la bannière sera en vue, la valeur sera vraie. Avec cette valeur booléenne, nous allons basculer (pass down) le bgColor et le transmettre à la barre de navigation. De cette façon, lorsque la bannière n'est pas visible, notre barre de navigation aura une couleur de fond de notre choix. Le fichier App.tsx jusqu'à présent ressemblera à ceci :

import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import Banner from './components/Banner'; import Navbar from './components/navbar/Navbar'; function App() { const [ onScreen, setOnScreen ] = useState(); const [ bgColor, setBGColor ] = useState("inherit"); useEffect(() => { if(!onScreen) setBGColor("var(--white)"); else if(onScreen) setBGColor("inherit"); }, [onScreen]) return ( <AppWrapper> <Navbar bgColor={bgColor}/> <Banner setOnScreen={setOnScreen}/> </AppWrapper> ); } export default App; const AppWrapper = styled.div` min-height: 100vh; display: flex width: 100vw; `;

Créons maintenant le Banner.tsx à l'intérieur des composants avec le code suivant :

import React, { useEffect } from 'react' import styled from 'styled-components' import bgImg from '../assets/office-designers-smallerx3.webp'; import Button from './Button'; import ContainerComponent from './ContainerComponent'; import { useInView } from 'react-intersection-observer'; const Banner = (props: any) => { const { ref, inView } = useInView({ rootMargin: "0px", threshold: 1, }); useEffect(() => { if(inView) props.setOnScreen(true); else if(!inView) props.setOnScreen(false); }, [inView, props]) return ( <BannerWrapper ref={ref}> <ContainerComponent column={true}> <BannerContent> <H1>YOU BRAND WILL GROW HERE</H1> <H2>Lorem ipsum, dolor sit</H2> <P>Lorem ipsum dolor sit amet consectetur adipisicing elit. Rem mollitia voluptatum minus deserunt delectus ipsum cupiditate. Unde, repudiandae molestiae? Mollitia temporibus unde beatae. Odio quod, accusamus dicta fugiat molestiae recusandae?</P> <a href="#about"><Button text={"discover"}/></a> </BannerContent> </ContainerComponent> </BannerWrapper> ) } export default Banner const BannerWrapper = styled.section` background: linear-gradient( rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.7) ), url(${bgImg}); background-position: bottom; background-repeat: no-repeat; background-size: cover; min-height: 100vh; width: 100%; padding-top: var(--top-margin); margin-top: 0; ` const BannerContent = styled.div` display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: white; margin-top: var(--top-margin); ` const H1 = styled.h1` font-size: 1.4rem; text-transform: uppercase; @media (max-width: 1440px) { font-size: 1.2rem; } @media (max-width: 1200px) { font-size: 1.1rem; } ` const H2 = styled.h2` font-size: 4rem; margin-bottom: 16px; text-transform: uppercase; color: var(--baby-blue-eyes); @media (max-width: 1440px) { font-size: 3.6rem; } @media (max-width: 1200px) { font-size: 3rem; } @media (max-width: 720px) { text-align: center; font-size: 2.6rem; } @media (max-width: 480px) { font-size: 2rem; } ` const P = styled.p` text-align: center; max-width: 500px; font-size: 1.2rem; margin-bottom: 100px; margin-top: 20px; line-height: 1.7; @media (max-width: 960px) { font-size: 1.1rem; line-height: 1.6; } @media (max-width: 480px) { font-size: 1rem; line-height: 1.3; } `

Il y a bien sûr des changements mineurs pour différentes tailles d'écran, mais à part ça, il n'y a rien de spécial comme vous pouvez le voir.

§About Section et Raisons Pour Lesquelles Notre Entreprise Existe

Cette section sera également très simple. Elle doit être plus propre que la bannière, on ne voudra pas fatiguer les yeux des visiteurs. Nous ajouterons une disposition en grille (grid) pour cette section avec une image sur le côté et le contenu de l'autre côté. Le contenu doit expliquer pourquoi l'entreprise a démarré, le but et peut-être un résumé de votre parcours jusqu'à présent. Le code complet du fichier About.tsx se trouve ici :

import React from 'react' import styled from 'styled-components' import cactusImg from '../assets/yellow-cactus-white-bg-smaller.webp'; const About = () => { return ( <AboutWrapper id="about"> <Img /> <TextBox> <TextBoxTitle> Lorem ipsum dolor sit, amet consectetur adipisicing elit. Modi molestias </TextBoxTitle> <TextBoxTitleText> Odio labore sequi consequatur ipsa cupiditate pariatur ducimus itaque consequuntur nulla nostrum!cupiditate pariatur ducimus itaque consequuntur nulla nostrum! </TextBoxTitleText> <div> <InfoBoxTitle> Some Title </InfoBoxTitle> <InfoText>Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium hic ipsa numquam quos ullam quo eum? Sit a dicta assumenda est doloremque unde eius consequuntur sint vero, dolorem non illo?</InfoText> </div> <div> <InfoBoxTitle> Another Title </InfoBoxTitle> <InfoText>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quae neque cupiditate consequuntur perferendis laboriosam mollitia vitae ipsum expedita. </InfoText> </div> </TextBox> </AboutWrapper> ) } export default About const AboutWrapper = styled.section` display:grid; grid-template-columns: 1fr 1fr; @media (max-width: 1200px) { grid-template-columns: 1fr; } ` const Img = styled.div` background-image: url(${cactusImg}); background-position: bottom; background-repeat: no-repeat; background-size: cover; width: 100%; padding-top: var(--top-margin); padding-bottom: var(--top-margin); @media (max-width: 1200px) { height: 500px; } @media (max-width: 960px) { height: 400px; } @media (max-width: 720px) { height: 300px; } ` const TextBox = styled.div` padding: 100px 140px 140px 20px; background: #ebebf5; display: grid; grid-template-columns: 1fr 1fr; grid-gap: 30px; @media (max-width: 1200px) { padding: 120px; } @media (max-width: 960px) { padding: 60px; } @media (max-width: 480px) { display: flex; flex-direction: column; } ` const TextBoxTitle = styled.h2` grid-column: span 2; margin-bottom: 20px; color: var(--darker-blue); font-size: 1.8rem; ` const InfoBoxTitle = styled.h3` font-size: 1.6rem; margin-bottom: 14px; ` const InfoText = styled.p` font-size: 1.1rem; line-height: 1.2; ` const TextBoxTitleText = styled.p` grid-column: span 2; font-size: 1.1rem; line-height: 1.2; `

La conception react est une partie importante du développement Web frontal (front-end). Au lieu d'opter pour les tailles d'écran les plus populaires, j'aime simplement vérifier ma conception sur différentes tailles d'écran. Si je vois que ça commence à mal paraître d'une manière ou d'une autre, je joue avec le design sur cette taille d'écran. Juste un petit conseil.

Importez maintenant ce fichier dans App.tsx sous le composant de bannière.

§Montrez Ce Que Nous Avons A Offrir

Dans cette section, nous allons présenter ce que nous avons à offrir à nos clients potentiels. Nous montrerons à quel point nous sommes rapides, comment nos développeurs / ingénieurs sont les meilleurs, etc. Ce composant que j'ai créé se compose de deux parties principales. La partie principale comportera des cartes qui révéleront plus d'informations lorsque les utilisateurs les survoleront (hover).

Pour garder notre composant propre et facile à lire, j'ai créé les données nécessaires dans un autre dossier. Dans le dossier src, créez un dossier appelé data. Il existe d'autres parties qui incluent un comportement similaire, nous allons donc créer différents fichiers de données supplémentaires ici dans ce dossier. Dans le dossier data, créez un fichier appelé offerData.ts. Dans ce fichier, nous aurons quelques icônes. Non, lancez ces commandes pour installer fontawesome et obtenir les icônes nécessaires :

npm i @fortawesome/fontawesome-svg-core @fortawesome/free-brands-svg-icons @fortawesome/free-solid-svg-icons @fortawesome/react-fontawesome

Nous pouvons maintenant commencer à importer et à utiliser nos icônes. Avec l'ajout des icônes, notre offerData.ts ressemblera à ceci :

import { faRocket, faCode, faRobot, faGift, IconDefinition } from '@fortawesome/free-solid-svg-icons'; interface OfferData { title: string text: string detail: string icon: IconDefinition }; const data:OfferData[] = [ { title: "We Are Fast", icon: faRocket, detail: "Lorem, ipsum dolor sit amet consectetur adipisicing elit. Accusantium eligendi assumenda deserunt ullam perspiciatis modi libero inventore earum voluptas iure?", text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere ab deserunt iste veritatis officia" }, { title: "Expert Developers", icon: faCode, detail: "Lorem, ipsum dolor sit amet consectetur adipisicing elit. Accusantium eligendi assumenda deserunt ullam perspiciatis modi libero inventore earum voluptas iure?", text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere ab deserunt iste veritatis officia" }, { title: "Lots of Promotions", icon: faGift, detail: "Lorem, ipsum dolor sit amet consectetur adipisicing elit. Accusantium eligendi assumenda deserunt ullam perspiciatis modi libero inventore earum voluptas iure?", text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere ab deserunt iste veritatis officia" }, { title: "Automize Your Growth", icon: faRobot, detail: "Lorem, ipsum dolor sit amet consectetur adipisicing elit. Accusantium eligendi assumenda deserunt ullam perspiciatis modi libero inventore earum voluptas iure?", text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere ab deserunt iste veritatis officia" }, ] export default data

Nous allons importer ces données dans notre fichier Offer.tsx et créer un élément de grille pour chaque objet à l'aide de la méthode "map()". Le code fini pour le fichier Offer.tsx aura le composant OfferBottom.tsx que nous n'avons pas encore créé. Donc, si vous voyez une erreur à cause de cela, ignorez-le simplement jusqu'à ce qu'il soit créé.

Maintenant, allez-y et créez un dossier dans components avec le nom offre. Nous allons créer nos 2 composants ici, les fichiers Offer.tsx et OfferBottom.tsx. Le code est ici pour le fichier Offer.tsx :

import React from 'react' import styled from 'styled-components' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import OfferBottom from './OfferBottom'; import data from '../../data/offerData'; const Offer = () => { return ( <> <OfferWrapper id='we-offer'> <Content> <OfferHeader> <OfferTitle> Lorem ipsum dolor sit amet consectetur adipisicing elit. </OfferTitle> <OfferSubTitle>Corporis modi optio, dolorum fugiat magni</OfferSubTitle> </OfferHeader> {data.map(d => <OfferBox key={d.title}> <DetailInfo> <DetailText>{d.detail}</DetailText> </DetailInfo> <OfferInfo> <IconSpan> <FontAwesomeIcon icon={d.icon}></FontAwesomeIcon> </IconSpan> <BoxTitle>{d.title}</BoxTitle> <BoxText>{d.text}</BoxText> </OfferInfo> </OfferBox>)} </Content> </OfferWrapper> <OfferBottom /> </> ) } export default Offer const OfferWrapper = styled.section` display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 100px; @media (max-width: 1200px) { padding:60px; } @media (max-width: 960px) { padding: 40px; } @media (max-width: 720px) { padding:20px; } ` const Content = styled.div` display: grid; grid-template-columns: repeat(4 ,1fr ); grid-gap: 30px; @media (max-width: 1200px) { grid-template-columns: 1fr 1fr; grid-gap: 10px; } @media (max-width: 720px) { grid-template-columns: 1fr; grid-gap: 6px; } ` const OfferHeader = styled.div` grid-column: 1/-1; text-align: center; margin-bottom: 60px; ` const OfferTitle = styled.h2` font-size: 1.8rem; margin-top: 50px; ` const OfferSubTitle = styled.p` margin-top:20px; ` const DetailInfo = styled.div` width: 100%; height: 0%; transition: 1s; opacity: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; ` const DetailText = styled.p` margin-bottom: 20px; line-height: 1.7; text-align: center; ` const OfferInfo = styled.div` display: flex; flex-direction: column; justify-content: center; ` const OfferBox = styled.div` transition : 0.4s; cursor: pointer; border-radius: 10px; :hover{ box-shadow:0 0 30px var(--nadeshiko-pink); ${DetailInfo} { height: 100%; opacity: 1; } ${OfferInfo} { transform: scale(0); } } padding: 40px 20px; @media (max-width: 1200px) { padding: 30px 16px; } @media (max-width: 720px) { padding: 12px 10; } ` const IconSpan = styled.span` font-size: 2.4rem; color: var(--baby-blue-eyes); ` const BoxTitle = styled.h3` margin-bottom: 26px; width: 100%; ` const BoxText = styled.p` line-height: 1.6; font-size: 1.07rem; `

Lorsque le visiteur survole la carte, il voit le contenu du texte derrière et il y a une animation de fondu / diapositive sympa. Si vous n'aimez pas quelque chose, n'hésitez pas à le changer, c'est votre projet.

La partie inférieure de cette section sera créée comme un autre composant et aura 2 images avec quelques informations. Nous utiliserons à nouveau la mise en page de la grille car il est tellement plus facile de gérer les mises en page à l'aide de la grille et c'est une bouée de sauvetage en matière de réactivité (responsiveness). Le code du fichier OfferBottom.tsx est le suivant :

import React from 'react' import styled from 'styled-components' import call from '../../assets/callcenter-employees-smaller.webp'; import meeting from '../../assets/office-people-smaller.webp'; const OfferBottom = () => { return ( <Wrapper> <Img src={call} alt="Call center team" /> <InfoBox> <AboveTitle> CONTACT US </AboveTitle> <InfoTitle> WE ARE READY TO HELP 24/7 </InfoTitle> <InfoText> Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nesciunt quis excepturi quia magnam dolores laudantium reiciendis eveniet, ab accusamus sequi? </InfoText> </InfoBox> <Img src={meeting} alt="a typical team meeting" /> <InfoBox> <AboveTitle> SIMPLE SOLUTIONS FOR COMPLEX PROBLEMS </AboveTitle> <InfoTitle> NO MATTER HOW DIFFICULT THE TASK </InfoTitle> <InfoText> Lorem ipsum dolor sit amet consectetur adipisicing elit. Possimus voluptates sed culpa ratione sapiente mollitia. </InfoText> </InfoBox> </Wrapper> ) } export default OfferBottom const Wrapper = styled.div` width: 100%; display: grid; grid-template-columns: repeat(4, 1fr); grid-gap: 10px; background: var(--black); @media (max-width: 1200px) { grid-template-columns: 1fr 1fr; } @media (max-width: 720px) { grid-template-columns: 1fr; } ` const Img = styled.img` height:100%; width:100%; object-fit: cover; ` const InfoBox = styled.div` padding: 20px 40px; ` const InfoTitle = styled.h3` color: var(--baby-blue-eyes); margin-bottom: 20px; font-weight: bold; font-size: 2rem; ` const InfoText = styled.p` font-size: 1.2rem; line-height: 1.6; color: var(--gray-2); ` const AboveTitle = styled.p` color: var(--gray-2); margin-top: 10px; `

Juste un rappel, vous pouvez consulter le repository si vous rencontrez des problèmes jusqu'à présent.

Importez maintenant Offer.tsx dans App.tsx et placez-le sous le composant about.

§Section de Portefeuille

Nous placerons notre section de portefeuille sous la section de l'offre. Vous verrez qu'au fur et à mesure que nous progressons, la complexité augmente. Maintenant, allez-y et créez un dossier portfolio dans le dossier components. Il y aura deux parties principales ici dans notre section portfolio. Dans la première, nous montrerons au visiteur les entreprises pour lesquelles nous avons créé des solutions.

Chaque entreprise pour laquelle nous avons travaillé sera représentée par une image, celles-ci devraient être des images d'entreprises ou quelque chose de similaire. Comme nous n'avons pas d'entreprises pour lesquelles nous avons créé des solutions, nous pouvons utiliser n'importe quelle image sympa que nous voulons. C'est ce que j'ai fait. Ces images sont placées dans une disposition de grille. When the visitors hover over an image the invisible element (opacity:0) that covers the image will slowly become visible and by clicking on it visitor will be able to visit the company's website. Bien sûr, dans notre cas, tous les liens sont simplement envoyés à la bannière.

Pour créer ce composant, nous aurons bien sûr besoin de certaines données. Maintenant, dans le dossier data, créez un fichier appelé portfolioData.ts. Le fichier que j'ai créé ressemble à ceci :

import mechanic from '../assets/black-white-mechanic-smaller.webp'; import view from '../assets/blue-red-view-smaller.webp'; import flower from '../assets/flower-blue-smaller.webp'; import lines from '../assets/lines-orange-white-smaller.webp'; import neuron from '../assets/neuron-smaller.webp'; import tree from '../assets/tree-green.webp'; import map from '../assets/map-pins-smaller.webp'; interface PortfolioData { img: string alt: string cols: string link: string }; const data:PortfolioData[] = [ {img: mechanic, alt: "some cool company", cols: "4", link: "#"}, {img: view, alt: "some cool company", cols: "4", link: "#"}, {img: map, alt: "some cool company", cols: "4", link: "#"}, {img: lines, alt: "some cool company", cols: "5", link: "#"}, {img: flower, alt: "some cool company", cols: "7", link: "#"}, {img: neuron, alt: "some cool company", cols: "6", link: "#"}, {img: tree, alt: "some cool company", cols: "6", link: "#"} ] export default data

Les valeurs "cols" déterminent l'espace qu'elles occuperont à l'intérieur de notre disposition de grille (grid). Nous n'avons pas à nous soucier de l'ordre dans lequel nous créons ces objets car nous allons définir "grid-auto-flow: row dense;". De cette façon, notre grille remplit les espaces vides pour nous. À quel point cela est cool!

Nous importerons ces données dans notre fichier Portfolio.tsx, qui se trouve dans le dossier portfolio. Pour garder le composant propre, nous allons créer un autre composant qui acceptera les informations et les affichera sous forme de cartes.

Nous allons créer le PortfolioItem.tsx, ce sera le composant qui prend les données comme accessoires. Nous mapperons (map) et créerons une carte pour chaque objet de données.

import React from 'react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faPaperPlane } from '@fortawesome/free-solid-svg-icons'; import styled from 'styled-components'; interface propTypes { imgSrc: string alt: string colSpan: string link: string } interface styleProp { colSpan: string } const PortfolioItem = (props:propTypes) => { return ( <Wrapper colSpan={props.colSpan}> <A href={props.link}> <FontAwesomeIcon icon={faPaperPlane}></FontAwesomeIcon> </A> <Img loading='lazy' src={props.imgSrc} alt={props.alt} /> </Wrapper> ) } export default PortfolioItem const A = styled.a` width: 100%; height: 100%; opacity: 0; position: absolute; background: rgba(255,255,255); transition: 0.4s; display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 1; font-size: 2rem; ` const Img = styled.img` width: 100%; height: 340px; object-fit: cover; ` const Wrapper = styled.div<styleProp>` position: relative; grid-column: span ${props => props.colSpan}; @media (max-width: 600px){ grid-column: 1/-1; } :hover { ${A} { opacity: 0.7; } } `

Nous pouvons maintenant créer le Portfolio.tsx, le code de ce composant est également très simple :

import React from 'react' import styled from 'styled-components' import ContainerComponent from '../ContainerComponent' import data from '../../data/portfolioData'; import PortfolioItem from './PortfolioItem'; import Numbers from './Numbers'; const Portfolio = () => { return ( <Wrapper id='portfolio'> <ContainerComponent column={true}> <H2>Some of The Companies That Love Our Solutions</H2> <SubTitle> Lorem ipsum dolor sit amet consectetur adipisicing elit. </SubTitle> <Grid> {data.map(d => { return <PortfolioItem key={d.img} imgSrc={d.img} alt={d.alt} colSpan={d.cols} link={d.link}/> })} </Grid> </ContainerComponent> <Numbers /> </Wrapper> ) } export default Portfolio const Wrapper = styled.section` ` const H2 = styled.h2` margin-top: 160px; text-align: center; width: 100%; font-size: 2.4rem; @media (max-width: 1200px){ margin-top: 100px; } @media (max-width: 960px){ margin-top: 80px; } ` const SubTitle = styled.p` text-align: center; margin-top: 20px; margin-bottom: 80px; ` const Grid = styled.div` display: grid; grid-template-columns: repeat(12, 1fr); grid-gap: 20px; grid-auto-flow: dense; align-items: center; @media (max-width: 600px){ grid-template-columns: 1fr; } `

Ignorez simplement l'erreur causée par le composant Numbers inexistant. Vous pouvez le commenter et voir à quoi il ressemble jusqu'à présent.

Nous allons commencer à travailler sur nos chiffres maintenant. Cette composante comprendra un décompte et un observateur d'intersection (intersection observer). Lorsque ce composant apparaîtra, nos chiffres commenceront à compter jusqu'aux chiffres que nous définirons. Créons maintenant le fichier numbersData.ts dans notre dossier data. Dans ce fichier nous aurons un simple tableau d'objets, le code de ce fichier est ici :

interface NumData { num: string text: string }; const data:NumData[] = [ {num: "2365", text: "Projects Completed"}, {num: "7012", text: "Happy Clients"}, {num: "10524", text: "Campaigns Automized"}, {num: "364", text: "Professionals"}, {num: "1842", text: "Cups of Coffee Taken"}, ] export default data

Pour faciliter notre travail au lieu de créer une fonction qui commencera à compter lorsque le composant apparaîtra, nous allons installer un package. Avec la commande bash npm i react-countup, nous pouvons installer le package nécessaire.

Cette partie sera également une grille et restituera un composant en tant qu'élément de grille. Notre composant doit accepter un nombre pour compter jusqu'à, un texte expliquant à quoi correspond le nombre et enfin une valeur booléenne pour vérifier si le composant est en vue. Le code complet du composant NumbersItem.tsx est le suivant :

import React, { useEffect, useState } from 'react' import styled from 'styled-components' import CountUp from 'react-countup'; interface PropTypes { num: string, text: string, inView: boolean } const NumbersItem = (props: PropTypes) => { const [ activated, setActivated ] = useState<boolean>(false); useEffect(() => { if(props.inView) setActivated(true) }, [props.inView]) return ( <Wrapper> <Num>{activated && <CountUp start={0} end={parseInt(props.num)} duration={2.5} />}</Num> <p>{props.text}</p> </Wrapper> ) } export default NumbersItem const Wrapper = styled.div` padding: 20px; display: flex; flex-direction: column; justify-content: center; @media (max-width: 480px){ align-items: center; } ` const Num = styled.p` font-size: 2.8rem; font-weight: bold; `

Nous pouvons maintenant créer le composant Numbers.tsx :

import React from 'react' import styled from 'styled-components' import ContainerComponent from '../ContainerComponent' import data from '../../data/numbersData' import NumbersItem from './NumbersItem' import { useInView } from 'react-intersection-observer' const Numbers = () => { const { ref, inView } = useInView({ rootMargin: "0px", threshold: 1, }); return ( <Wrapper> <ContainerComponent column={false}> <Grid ref={ref}> {data.map(d => <NumbersItem key={d.num} inView={inView} num={d.num} text={d.text}/>)} </Grid> </ContainerComponent> </Wrapper> ) } export default Numbers const Wrapper = styled.div` margin-top: 160px; margin-bottom: 40px; width: 100%; background: linear-gradient(0.25turn, var(--orchid-pink), var(--baby-blue-eyes)); min-height: 300px; display: flex; aşign-items: center; ` const Grid = styled.div` flex-grow: 1; display: grid; grid-template-columns: repeat(5, 1fr); grid-gap: 40px; @media (max-width: 1200px) { grid-gap: 20px } @media (max-width: 960px) { grid-template-columns: repeat(3, 1fr); grid-gap: 10px } @media (max-width: 600px) { grid-template-columns: 1fr 1fr; } @media (max-width: 480px) { grid-template-columns: 1fr; } `

Maintenant, nous avons géré notre section de portefeuille. Si tout va bien jusqu'à présent, vous devriez voir les chiffres commencer à compter lorsque ce composant apparaît. N'oubliez pas d'importer le Portfolio.tsx dans App.tsx et placez-le sous la section offre.

§Travaux Récents et Marques avec Lesquelles Nous Avons Travaillé

Dans cette section, nous montrerons à nos visiteurs les articles de blog écrits dans notre blog et certaines marques des entreprises avec lesquelles nous avons travaillé. Créons maintenant notre fichier de données le plus simple à ce jour. Dans le dossier data, créez le fichier brandData.ts. Dans le fichier, nous importerons simplement quelques images de marque. J'ai créé quelques marques inventées à utiliser ici :

import awesome from '../assets/awesome-logo-smaller.webp'; import cool from '../assets/cool-brand-smaller.webp'; import international from '../assets/international-brand-smaller.webp'; import quality from '../assets/quality-brand-smaller.webp'; import known from '../assets/well-known-brand-smaller.webp'; interface BrandData { img: string }; const data:BrandData[] = [ {img: awesome}, {img: cool}, {img: known}, {img: international}, {img: quality } ] export default data

Vous pouvez également ajouter ici des informations supplémentaires telles que le nom de la marque ou de petites informations sur la marque. Je suppose que tout le monde peut reconnaître mes marques inventées ici et n'a rien inclus. Je pense que cela ressemble mieux à cela, mais encore une fois, changez la partie qui, selon vous, peut être améliorée.

Nous allons avoir besoin d'un autre fichier pour les données récentes des blogs. Dans le dossier data, créez maintenant recentWorkCardData.ts. Ce fichier exportera un tableau d'objets. Chaque objet a un titre, un texte expliquant le post, le nom de l'auteur et un avatar de l'auteur. Le code complet est ici :

import woman from '../assets/woman-avatar-smaller.webp' interface recentWorkCardData { title: string text: string name: string avatar: string } const data:recentWorkCardData[] = [ { title: "5 Methods to Increase Visibility", text: "Praesent porta enim augue, et iaculis sem scelerisque id. Nulla sagittis fringilla imperdiet. Donec vestibulum leo a diam pharetra, vel ullamcorper mauris convallis.", name: "Jane Doe", avatar: woman }, { title: "How To Gain More Customers", text: "Praesent porta enim augue, et iaculis sem scelerisque id. Nulla sagittis fringilla imperdiet. Donec vestibulum leo a diam pharetra, vel ullamcorper mauris convallis.", name: "Jane Doe", avatar: woman }, { title: "Free AI Tools You Can Use Today", text: "Praesent porta enim augue, et iaculis sem scelerisque id. Nulla sagittis fringilla imperdiet. Donec vestibulum leo a diam pharetra, vel ullamcorper mauris convallis.", name: "Jane Doe", avatar: woman }, ]; export default data;

Ces données seront mappées (mapped) et rendues dans un composant appelé RecentWorkCard.tsx. Créez un dossier appelé recentWork et créez le RecentWorkCard.tsx ici. Le code de ce composant est également simple :

import React from 'react' import styled from 'styled-components' import lines from '../../assets/lines-orange-white-smaller.webp' interface PropTypes { title: string text: string name: string avatar: string } const RecentWorkCard = (props:PropTypes) => { return ( <Wrapper> <InfoBox> <H3>{props.title}</H3> <WriterP>{props.text}</WriterP> </InfoBox> <WriterBox> <Img src={props.avatar} alt="" /> <WriterInfo> <p>{props.name}</p> <p>Sr. Web Developer</p> </WriterInfo> </WriterBox> </Wrapper> ) } export default RecentWorkCard const InfoBox = styled.div` margin-bottom: 20px; transition: 0.4s; ` const Wrapper = styled.div` width: 100%; border-radius: 5px; padding: 30px; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; min-height: 400px; background: linear-gradient( rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7) ), url(${lines}); background-position: bottom; background-repeat: no-repeat; background-size: cover; color: white; :hover { cursor: pointer; ${InfoBox} { color: var(--baby-blue-eyes); } } ` const WriterBox = styled.div` width: 100%; display: grid; grid-template-columns: repeat(4, 1fr); grid-gap: 20px; ` const WriterInfo = styled.div` height: 100%; display: flex; flex-direction: column; justify-content: space-between; ` const H3 = styled.h3` font-size: 1.4rem; @media (max-width: 960px){ font-size: 2rem; } margin-bottom: 20px; ` const WriterP = styled.p` margin-top: 6px; @media (max-width: 960px){ font-size: 1.4rem; } ` const Img = styled.img` width: 100%; height: 60px; border-radius: 5px; object-fit: cover; `

Comme vous pouvez le voir, il n'y a rien d'extraordinaire ici.

Nous pouvons continuer et créer également le fichier RecentWork.tsx. Même si nous n'avons pas encore le volet marques. Ignorez simplement l'erreur causée par son absence. Le code de ce fichier est ici :

import React from 'react' import styled from 'styled-components' import ContainerComponent from '../ContainerComponent' import data from '../../data/recentWorkCardData' import RecentWorkCard from './RecentWorkCard' import Brands from './Brands' const RecentWork = () => { return ( <Wrapper id='recent-work'> <ContainerComponent column={true}> <H2>Our most recent publications</H2> <SubTitle>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Id quas maxime a doloremque esse.</SubTitle> <RecentWorkGrid> {data.map(d => <RecentWorkCard key={d.title} title={d.title} text={d.text} name={d.name} avatar={d.avatar} />)} </RecentWorkGrid> <Brands /> </ContainerComponent> </Wrapper> ) } export default RecentWork const Wrapper = styled.section` width: 100%; margin-bottom: 60px; margin-top: 120px; ` const H2 = styled.h2` width: 100%; text-align: center; font-size: 2.8rem; font-weight: thin; ` const SubTitle = styled.p` margin-top: 6px; text-align: center; font-size: 1.2rem; color: var(--gray-2); ` const RecentWorkGrid = styled.div` display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 60px; width: 100%; margin: auto; margin-top: 60px; @media (max-width: 960px){ grid-template-columns: 1fr; } `

Encore une fois, rien de bien compliqué.

Il est temps de créer la partie qui contient la marque. Le code du fichier Brands.tsx est ici :

import React from 'react' import styled from 'styled-components' import data from '../../data/brandData' import BrandItem from './BrandItem' const Brands = () => { return ( <Wrapper> <H3>You may know our partners</H3> {data.map(d => <BrandItem img={d.img} />)} </Wrapper> ) } export default Brands const Wrapper = styled.div` margin-top: 120px; width: 100%; display: grid; grid-template-columns: repeat(5, 1fr); grid-gap: 100px; @media (max-width: 1200px) { grid-gap: 20px; } @media (max-width: 720px) { grid-template-columns: repeat(3, 1fr); grid-gap: 30px; } @media (max-width: 480px) { grid-template-columns: 1fr 1fr; grid-gap: 20px; } ` const H3 = styled.h3` grid-column: 1/ -1; margin-bottom: 0; font-size: 1.8rem; margin-left: 10px; `

Maintenant, le composant que nous allons utiliser pour afficher toutes nos marques dans le fichier BrandItem.tsx :

import React from 'react' import styled from 'styled-components' interface PropTypes { img: string } const BrandItem = (props:PropTypes) => { return ( <Wrapper> <Img src={props.img} alt="" /> </Wrapper> ) } export default BrandItem const Wrapper = styled.div` padding: 10px; border-radius: 5px; display: flex; flex-direction: column; align-items: center; ` const Img = styled.img` width: 100%; transition: 0.4s; max-width: 160px; border-radius: 5px; :hover { box-shadow: 0px 0px 32px var(--nadeshiko-pink); } `

Avec ce composant ajouté, nous avons finalisé nos travaux récents. Importez ce composant dans App.tsx et affichez-le dans la section portfolio.

§Section de Contact et Notre Emplacement

Nous devons bien sûr donner aux visiteurs la possibilité de nous contacter facilement quand ils le souhaitent et de montrer notre emplacement. La section de contact sera composée de ces deux parties principales. Créons un dossier dans les components appelé contact et commençons.

Dans le dossier contact, nous pouvons commencer par créer notre carte. Au lieu d'utiliser Google Maps, j'ai décidé d'utiliser mapbox pour cette carte. N'oubliez pas d'obtenir votre clé api pour pouvoir utiliser cette carte. Enregistrez votre clé API dans le fichier .env.local à la racine de votre projet. J'ai nommé ma clé API REACT_APP_MAPBOX_ACCESS_TOKEN. L'initialisation de la variable avec "REACTAPP" nous la rend visible dans notre application.

Il y avait quelques problèmes de performances, mais maintenant je pense que c'est acceptable en termes de performances. Nous utiliserons un web worker pour ne pas bloquer le thread principal. Cela permet d'augmenter les performances dans cette partie. Il y aura un bouton pour mettre la carte en plein écran, une épingle indiquant l'emplacement de notre bureau et un autre bouton pour montrer l'emplacement du visiteur. Installons maintenant les packages nécessaires pour cette partie avec cette commande :

npm i mapbox-gl react-map-gl worker-loader

Dans le dossier contact, créez le fichier MapComponent.tsx et ajoutez ce code :

import React from 'react'; import styled from 'styled-components'; import ReactMapGL, { Marker, FullscreenControl, GeolocateControl, NavigationControl } from 'react-map-gl'; import pin from '../../assets/pin.png' import mapboxgl from 'mapbox-gl'; // @ts-ignore // eslint-disable-next-line import/no-webpack-loader-syntax mapboxgl.workerClass = require('worker-loader!mapbox-gl/dist/mapbox-gl-csp-worker').default; const MapComponent = () => { return ( <Wrapper> <ReactMapGL initialViewState={{ latitude: 48.87, longitude: 2.3, zoom: 5.5 }} mapStyle="mapbox://styles/mapbox/streets-v9" style={{ height:"100%", width: "100%"}} attributionControl={false} mapboxAccessToken={process.env.REACT_APP_MAPBOX_ACCESS_TOKEN} > <GeolocateControl positionOptions={{enableHighAccuracy: true}} trackUserLocation={false} /> <FullscreenControl /> <Marker longitude={2.3} latitude={48.87} anchor="bottom" > <img style={{width:"21px", height:"14px",top:"-11px", bottom: "-7px"}} src={pin} alt="pin" /> </Marker> <NavigationControl visualizePitch={true}/> </ReactMapGL> </Wrapper> ); }; export default MapComponent const Wrapper = styled.div` width: 100%; height: 100%; @media (max-width: 960px) { grid-column: 1/-1; } min-height: 400px; `

Il y a beaucoup de personnalisations et de configurations qui peuvent être faites dans la carte. Je vous suggère de lire les documents officiels et d'explorer vos options. Je suis sûr que vous en aurez une certaine utilité à l'avenir.

Passons maintenant à notre formulaire de contact. Puisque nous n'avons pas de backend ou de moyens pour gérer notre envoi (mailing), nous enregistrerons (loghing) simplement les données qui nous sont fournies par les visiteurs. Créez un fichier appelé ContactForm.tsx et ajoutez le code suivant :

import React, { useState } from 'react' import styled from 'styled-components' import Button from '../Button' const ContactForm = () => { const [name, setName] = useState<string>(""); const [email, setEmail] = useState<string>(""); const [subject, setSubject] = useState<string>(""); const [text, setText] = useState<string>(""); const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); console.log("the info to send", name, email, subject, text) } return ( <Wrapper> <H3>Feel free to contact us!</H3> <P>We will respond to you as soon as we can</P> <Form onSubmit={(e) => handleSubmit(e)}> <Label htmlFor="f-name"> Enter your name <Input required type="text" name="f-name" placeholder='John Doe' onChange={(e) => setName(e.target.value)} /> </Label> <Label htmlFor="f-email"> Enter your email address <Input type="email" name="f-email" required placeholder='example@gmail.com' onChange={(e) => setEmail(e.target.value)} /> </Label> <Label htmlFor="f-subject"> Enter a subject <Input type="text" name="f-subject" required placeholder='Marketing campaign' onChange={(e) => setSubject(e.target.value)} /> </Label> <Label htmlFor="f-textarea"> What would you like to share with us </Label> <Textarea required rows={12} onChange={(e) => setText(e.target.value)} /> <Button text='submit'/> </Form> </Wrapper> ) } export default ContactForm const Wrapper = styled.div` width: 100%; display: flex; flex-direction: column; padding: 20px 0; ` const H3 = styled.h3` font-size: 1.8rem; ` const P = styled.p` color: var(--gray-2); ` const Form = styled.form` margin-top: 10px; width: 100%; display: flex; flex-direction: column; ` const Label = styled.label` font-size: .8rem; margin-top: 14px; ` const Input = styled.input` width: 100%; padding: 8px; font-size: 1.4rem; transition: 0.4s; :focus { box-shadow: 0px 0px 32px var(--baby-blue-eyes); } ` const Textarea = styled.textarea` width: 100%; padding: 10px; font-size: 1.1rem; transition: 0.4s; :focus { box-shadow: 0px 0px 32px var(--baby-blue-eyes); } margin-bottom: 10px; `

Rien de spécial dans cette partie. Comme vous pouvez le voir, nous avons trouvé une autre utilisation pour le composant de bouton que nous avons créé, c'est la raison pour laquelle créer des composants au lieu de répéter du code est très simple.

Nous allons maintenant créer le fichier Contact.tsx et importer ces deux composants que nous venons de créer. Le code de ce composant est ici :

import React from 'react' import styled from 'styled-components' import ContactForm from './ContactForm' import ContainerComponent from '../ContainerComponent' import Map from './MapComponent' const Contact = () => { return ( <ContainerComponent column={false}> <Wrapper id='contact'> <Map /> <ContactForm /> </Wrapper> </ContainerComponent> ) } export default Contact const Wrapper = styled.section` display: grid; grid-template-columns: 1fr 1fr; width: 100%; min-height: 600px; grid-gap: 40px; @media (max-width: 960px) { grid-template-columns: 1fr; } `

Ça y est, nous avons maintenant une section de contact sympa. Cette partie est également réactive comme toutes les autres parties que nous avons créées jusqu'à présent. S'il y a quelque chose que vous pensez pouvoir améliorer, allez-y !

Importez ce composant sous les travaux récents. Dans index.CSS, ajoutez le code suivant dans la première ligne afin d'afficher correctement la carte :

@import 'mapbox-gl/dist/mapbox-gl.CSS';

§La Section de Pied de Page

Le pied de page est la partie que votre visiteur verra en dernier. Notre pied de page aura 3 parties principales. Une partie pour les visiteurs de nous contacter par téléphone. Une autre partie pour demander à nos visiteurs de s'abonner à notre newsletter. La dernière partie pour le bas du pied de page qui comprendra le texte du droit d'auteur et les liens de médias sociaux permettant aux visiteurs de suivre notre entreprise à partir des médias sociaux.

Commençons par créer notre dossier footer dans le dossier components. Le premier fichier que nous allons créer pour cette partie est Contact.tsx. Comme le code est très basique, je vais juste le partager avec vous tout de suite :

import React from 'react' import styled from 'styled-components' const ContactComponent = () => { return ( <Contact> <H3>Let's talk!</H3> <Text>Feel free to contact us 24/7. We can discuss an ongoing project or start talking about a new one. You will be a ble to reach an employee whenever you call.</Text> <Number>0555-5555-555555-555</Number> <Number>0555-5555-555555-554</Number> </Contact> ) } export default ContactComponent const Contact = styled.div` padding: 100px 0px 120px; @media (max-width: 960px){ padding: 40px 0 0 0 ; } ` const H3 = styled.h3` color: var(--white); font-size: 2rem; margin-bottom: 40px; ` const Text = styled.p` margin-bottom: 20px; ` const Number = styled.p` color: var(--baby-blue-eyes); font-size: 1.4rem; `

Créons maintenant notre newsletter. Nous n'avons pas non plus de backend pour cette partie, donc nous enregistrerons (log) simplement l'adresse e-mail que le visiteur nous a donnée. Créez le fichier Newsletter.tsx ici et ajoutez le code suivant :

import React, { useState } from 'react' import styled from 'styled-components'; import Button from '../Button'; const NewsletterComponent = () => { const [email, setEmail] = useState<string>(""); const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); console.log("sent email is ==> ",email) } return ( <Newsletter> <H3>Sign up to our newsletter.</H3> <Text>If you wanna find out the latest tips and tricks we think that can help your business grow, sign up to our newsletter. You can subscribe easily if you don't like our weekly emails, no worries.</Text> <Form onSubmit={e => handleSubmit(e)}> <label htmlFor=""> <Input type="email" required onChange={e => setEmail(e.target.value)} /> </label> <Button text='Sign Up'/> </Form> </Newsletter> ) } export default NewsletterComponent; const H3 = styled.h3` color: var(--white); font-size: 2rem; margin-bottom: 40px; ` const Text = styled.p` margin-bottom: 20px; margin-left: auto; ` const Newsletter = styled.div` padding: 100px 0px 120px; width: 100%; @media (max-width: 960px){ padding: 0 ; } ` const Form = styled.form` margin-top: 10px; width: 100%; display: flex; flex-direction: column; ` const Input = styled.input` margin-top: 20px; margin-bottom: 20px; width: 100%; padding: 8px; font-size: 1.4rem; transition: 0.4s; :focus { box-shadow: 0px 0px 32px var(--baby-blue-eyes); } `

Comme vous pouvez le voir juste un simple formulaire.

Pour la partie inférieure de notre pied de page, nous utiliserons des icônes de fontawesome. Cette partie dirigera les visiteurs vers mon blog et mon profil github si vous ne modifiez pas les valeurs href, alors allez-y et modifiez-les. Le code du fichier FooterBottom.tsx est ici :

import React from 'react' import styled from 'styled-components' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faFacebook, faTwitter, faGithub } from '@fortawesome/free-brands-svg-icons'; import { faBlog } from '@fortawesome/free-solid-svg-icons'; const FooterBottomComponent = () => { return ( <FooterBottom> <Icons> <IconA href="/#"> <FontAwesomeIcon icon={faFacebook} /> </IconA> <IconA href="https://wwizard-blog.netlify.app/"> <FontAwesomeIcon icon={faBlog} /> </IconA> <IconA href="https://github.com/ilci66"> <FontAwesomeIcon icon={faGithub} /> </IconA> <IconA href="/#"> <FontAwesomeIcon icon={faTwitter} /> </IconA> </Icons> <Text>You can find the full code <A href='#'>here</A> and the blog post <A href='/#'>here</A>. Ilker AKBIYIK</Text> </FooterBottom> ) } export default FooterBottomComponent const FooterBottom = styled.div` width: 100%; display: flex; flex-direction: row; grid-column: 1/-1; align-items:center; @media (max-width: 960px){ padding-top: 20px; } @media (max-width: 720px){ flex-direction: column; } ` const A = styled.a` color: var(--baby-blue-eyes); text-decoration: none; ` const Icons = styled.div` display: flex; flex-direction: row; @media (max-width: 720px){ width: 100%; justify-content: space-between; } ` const IconA = styled.a` font-size: 2.4rem; color: var(--baby-blue-eyes); background: var(--gray-2); margin: 10px; padding: 10px 20px; transition: 0.4s; :hover { background: var(--white); color: var(--nadeshiko-pink); } @media (max-width: 600px){ padding: 6px 8px; margin: 4px; } ` const Text = styled.p` margin-bottom: 20px; margin-left: auto; @media (max-width: 720px){ margin: auto; } `

Nous pouvons maintenant conclure (wrap) tout cela avec le fichier Footer.tsx. Ce fichier gérera principalement le placement des composants. Le code est ici :

import React from 'react' import styled from 'styled-components' import ContainerComponent from '../ContainerComponent' import Contact from './Contact'; import Newsletter from './Newsletter'; import FooterBottomComponent from './FooterBottom'; const Footer = () => { return ( <Wrapper> <ContainerComponent column={true}> <ContactNewsletter> <Contact /> <Newsletter /> </ContactNewsletter> <FooterBottomComponent /> </ContainerComponent> </Wrapper> ) } export default Footer const Wrapper = styled.section` width: 100%; background: var(--gray); margin-top: 40px; color: var(--gray-2); @media (max-width: 960px){ padding-bottom: 40px; } ` const ContactNewsletter = styled.div` display: grid; grid-template-columns: 1fr 1fr; grid-gap: 10vw; width: 100%; @media (max-width: 960px){ grid-template-columns: 1fr; grid-gap: 20px; } `

Ajoutez ce composant dans App.tsx et enfin notre fichier App.tsx ressemblera à ceci :

import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import Banner from './components/Banner'; import Navbar from './components/navbar/Navbar'; import About from './components/About'; import Offer from './components/offer/Offer'; import Portfolio from './components/portfolio/Portfolio'; import RecentWork from './components/recentWork/RecentWork'; import Contact from './components/contact/Contact'; import Footer from './components/footer/Footer'; function App() { const [ onScreen, setOnScreen ] = useState(); const [ bgColor, setBGColor ] = useState("inherit"); useEffect(() => { if(!onScreen) setBGColor("var(--white)"); else if(onScreen) setBGColor("inherit"); }, [onScreen]) return ( <AppWrapper> <Navbar bgColor={bgColor}/> <Banner setOnScreen={setOnScreen}/> <About /> <Offer /> <Portfolio /> <RecentWork /> <Contact /> <Footer /> </AppWrapper> ); } export default App; const AppWrapper = styled.div` min-height: 100vh; display: flex width: 100vw; `;

§Conclusion

Je pense que ce projet est très convivial pour les débutants et comprend encore une certaine complexité pour que vous puissiez améliorer vos compétences de react. Il est un peu peu conventionnel de créer un type de projet de page de destination en utilisant reactjs au lieu de HTML et CSS simples, mais comprendre qu'avec react, vous pouvez toujours créer facilement un site Web comme celui-ci était amusant. Vous verrez qu'il y a des problèmes de performances que vous pouvez résoudre en faisant simplement quelques recherches sur Google. Certains sont simples comme le redimensionnement (resizing) des images et l'utilisation du bon format.

L'utilisation de styled components peut vous coûter cher en termes de performances. Si vous aviez développé ce projet en utilisant du CSS simple, votre site Web serait légèrement plus rapide. En tant que développeur, vous devez prendre les décisions pour chaque projet concernant les outils à utiliser. Vous devez avoir de l'expérience avec plusieurs outils pour savoir ce que vous devez utiliser pour chaque projet. Chaque projet est différent avec des nécessités et des problèmes potentiels différents.

J'espère que vous avez acquis des connaissances et que vous vous êtes amusé à développer ce projet. Ne vous arrêtez pas ici, améliorez-le, ajoutez d'autres pages, créez un backend, gérez les mailings, etc. Vous êtes libre de l'utiliser comme modèle pour vos futurs projets.

Ilker Akbiyik