Aller au contenu
  1. Projets/

Booki (Page d'accueil d'une agence de voyage)

·3997 mots·19 mins
HTML CSS Front-end Bootcamp OC Débutant
Auteur
Gustave
Je suis un développeur web passionné par l’élégance du TypeScript.
Mes projets réalisés en formation chez OpenClassrooms - Cet article fait partie d'une série.
Partie 1: Cet article

Tout premier projet du parcours de développeur web d’Open Classrooms, Booki consiste en l’intégration de maquettes Figma (version mobile, tablette et PC).

C’est l’occasion parfaite de commencer à créer des pages web et apprendre des notions comme le HTML sémantique, ou encore les variables et sélecteurs CSS !


Booki #

Résultat du projet #

Page web #

Bannière de Booki

Cliquez sur le bouton ci-dessous pour découvrir la page web que j'ai réalisée et accédez-y dès maintenant !

  👉 Lancer la démo !


Code source (GitHub) #

Cliquez sur l’encadré ci-dessous pour accéder au code source de mon projet.
 
gustaveWPM/OC-Booki

HTML/CSS project made during an OpenClassrooms bootcamp

CSS
0
0


Pistes pour la réalisation du projet #

Cet article n’est pas un guide pas-à-pas !
Il s’agit d’un condensé des réflexions que je me suis faites.

Vous ne comprenez pas un point dans cet article ? 🤔
Vous venez d’identifier une de vos lacunes (et c’est une bonne chose).
Dans ce cas : travaillez les ressources fournies avant de poursuivre.


Afin de réaliser ce projet, il m’a fallu expérimenter en HTML et en CSS.
Cela a comporté son lot d’essais et de surprises, et donc de choix à partager.

Je vous recommande de garder dans l’un de vos onglets cette page web :
🔗 Maquettes Figma de Booki (PC, tablette, mobile)

Bien commencer #

Avant de commencer à travailler, je vous recommande de comprendre (dans les grandes lignes) la syntaxe du HTML.
⬇ Vous trouverez en annexe une sélection de ressources pédagogiques

Avant de se lancer… (HTML) #

⬇ Passer directement au CSS

Comme vous le constaterez, l’indentation (espaces en début de ligne) est un sujet qui revient en permanence avec le HTML.

Pour ce projet, vous n’aurez pas besoin d’extensions particulières dans votre éditeur de code.

Je vous recommande juste de vous assurer que votre formatter fonctionne correctement afin de pouvoir rapidement indenter automatiquement votre code.

Dans VS Code, utilisez la combinaison de touches suivante :
CTRL+Shift+P*, puis appelez la commande Format Document.
* Le raccourci clavier sera Cmd+Shift+P sur Mac.
Capture d'écran de la commande VSCode à sélectionner après avoir recherché : « Format Document »

Capture d’écran de la commande VS Code « Format Document ».

Pour que cela se fasse automatiquement, vous pouvez activer l’option Format on Save de VSCode.

Et si vous souhaitez utiliser un autre formatter que celui de VSCode…
🔗 Ma configuration Prettier


Vous pouvez également vous épauler d’une IA :

N’utilisez jamais du code que vous n’arrivez pas à raisonner !
N’hésitez pas à demander à votre agent conversationnel de reformuler sa réponse afin qu’elle soit plus simple, ou plus élégante, au moindre doute.
Retour aux fondamentaux #
Vous trouverez toutes les ressources pédagogiques pour obtenir des fondamentaux solides en HTML ⬇ dans les annexes de cet article de blog.

Néanmoins, je me permets deux petits conseils :

N'oubliez pas le fil d'ariane en haut de votre éditeur dans VSCode

N’oubliez pas le fil d’ariane en haut de votre éditeur dans VS Code !

Je ne l’ai découvert que très tard malgré le fait qu’il était sous mon nez depuis de longues heures, et c’est parfois un outil de plus qui s’avère utile pour se repérer.

Découvrez les Emmet Shortcuts, et vous n'écrirez plus jamais votre code HTML de la même manière...

De la maquette au HTML #

🔗 Aujourd’hui, OpenClassrooms fournissent un squelette de code HTML dans l’énoncé de Booki. Ce n’était pas le cas lorsque j’avais réalisé mon projet.

Outre ce point de détail, l’idée reste la même : prendre la maquette, et l’annoter pour pouvoir la transcrire en HTML.

Maquette de Booki, annotée pour la transcrire en HTML

Maquette de Booki, annotée pour visualiser comment la transcrire en HTML.

  • L’annotation de vos maquettes avec des balises HTML permet de clarifier la structure et l’organisation de votre site web. Cela facilite la compréhension de la conception de votre site.

  • En annotant vos maquettes avec des balises HTML, vous créez une référence claire pour le codage ultérieur. Cela facilite la tâche des développeurs qui travaillent sur l’intégration, car ils comprennent immédiatement comment la conception doit être traduite en code.

La sémantique (SEO) #

Voici l’ensemble des éléments de sémantique HTML que j’ai choisi d’utiliser pour ce projet (chaque élément de la liste est cliquable et vous renverra directement vers une page de documentation) :

Testez vos meta description et title grâce à un simulateur de SERP.
🔗 Google SERP Simulator

Votre utilisation des balises <ul> et <li> peut également s’avérer sémantique !
🔗 Discussion sur Stack Overflow à ce sujet
 
Faites aussi attention lors de votre utilisation des balises <section> et <article>. Celles-ci doivent impérativement contenir au moins une balise <hN> afin de totalement respecter les standards W3C.

Le Validateur W3C (HTML) #

Le validateur W3C est un outil essentiel, car il vous permettra de vérifier si votre code HTML et CSS respectent les normes établies par le World Wide Web Consortium (W3C).

Cela vous aidera à améliorer la qualité de votre code, à améliorer la compatibilité entre différents navigateurs et rendre votre site web plus accessible aux personnes handicapées.

De plus, le respect des normes du W3C peut contribuer positivement au référencement d’un site web (SEO).

👉 Vérifier mon HTML


Avant de se lancer… (CSS) #

⬆ Avant de vous lancer, suivez très exactement les mêmes conseils que pour ce qui est de vos premiers pas en HTML tels qu’ils sont décrits ci-dessus dans cet article.


Fondamentaux #
Vous trouverez toutes les ressources pédagogiques pour obtenir des fondamentaux solides en CSS ⬇ dans les annexes de cet article de blog.
Sélecteurs #

L’importance des sélecteurs CSS réside dans leur capacité à apporter flexibilité et précision lorsqu’il s’agit de styliser les pages web.

En utilisant des sélecteurs bien choisis, il est possible de définir des styles pour des éléments individuels, des classes d’éléments ou des groupes d’éléments, ce qui permet de personnaliser l’apparence d’une page de manière signifiante.

👉 Réviser les sélecteurs CSS


Ces sélecteurs peuvent également s’avérer utiles pour l’écriture d’outils de scraping ou de tests automatisés.

Par exemple :

Regarder un peu de ce côté pourrait vous donner une approche complémentaire quant aux sélecteurs CSS, et notamment comprendre l’importance et l’utilité des attributs id (ou data) pour la réalisation de tests automatisés.

Validateur W3C (HTML/CSS) #

⬆ Nous avons déjà présenté le validateur W3C un peu plus tôt dans cet article.

Cependant, comme la page du validateur permettant d’activer ou non l’option « CSS » n’est pas évidente à trouver du premier coup…

👉 Vérifier mon CSS


Choix personnels #

Il n’était pas autorisé d’utiliser SASS, ni de JavaScript, ni de bundler

Rappels des contraintes (arbitraires) de l’exercice #

Ce projet imposait la réalisation d’un projet totalement vanilla.
C’est-à-dire qu’il ne fallait rendre que deux fichiers : le fichier index.html et le fichier style.css.

Cependant, je souhaitais tout de même produire un code relativement modulaire.

Concernant le HTML #

Le code de ce projet est assez classique, et une bonne compréhension du HTML est suffisante pour en lire le code.
La seule particularité à noter est que j’ai ici fait un choix plutôt trivial quant à l’optimisation des images : afficher les images avec une meilleure qualité selon la largeur du viewport… mais à l’inverse de ce qui aurait dû être fait : moins le viewport est large, plus la résolution des images affichées est grande.

Je souhaitais que les images de la page d’accueil soient correctement affichées sur ordinateur lorsque l’utilisateur zoome.

Plus l’on zoome, moins le viewport est large.

Mais cela signifie aussi que les versions mobile/tablette doivent charger des images plus lourdes, car de meilleure qualité. On a donc un impact très négatif à cause de ce choix : au sacrifice de conserver un bon affichage de la page d’accueil lorsque celle-ci est zoomée sur ordinateur, on n’est pas très “Green tech” envers les mobiles et tablettes… !

🔗 Comprendre le Viewport dans le Web mobile

Ce serait plutôt l’inverse que l’on chercherait à prioriser : avoir la page la plus légère possible pour les téléphones et tablettes.
Une catastrophe en termes de Page speed ? #

Pour rappel : c’est l’impossibilité d’utiliser du JavaScript qui m’a fait trancher pour ce choix (contrainte arbitraire du projet…).

Malgré ce choix, les Page speeds restent très bons à l’heure où j’écris ces lignes.

Pagespeed de la page d'accueil de Booki sur PC

Page speed de la version ordinateur de la page d’accueil de Booki.

Pagespeed de la page d'accueil de Booki sur mobile

Page speed de la version téléphone de la page d’accueil de Booki.

Regarder ce que donnent les benchmarks des Pages speed plutôt que de partir sur des a priori #

Cela est notamment lié au design de la version mobile où seulement une image est chargée sur téléphone au-dessus de la ligne de flottaison.

Les images ont été passées du JPEG au WEBP, et le logo du PNG au SVG.
En l’occurrence, cela n’a pas changé grand chose aux indicateurs.

 

J’ai tout de même ajouté des attributs loading="lazy" entre temps (c’est au moins ça de pris)…

Aucune de ces images ne sont dans le CSS sous forme de background image : elles sont bien toutes présentes dans des balises HTML.

Par exemple :

<a href="#">
  <div class="lodging-card-wrapper has-tweakers">
    <article class="lodging-card">
      <picture class="thumbnail">
        <source srcset="./images/hebergements/2_large/marcus-loke-WQJvWU_HZFo-unsplash.jpg" media="(max-width: 768px)" />
        <source srcset="./images/hebergements/3_medium/marcus-loke-WQJvWU_HZFo-unsplash.jpg" media="(max-width: 1281px)" />
        <img src="./images/hebergements/4_small/marcus-loke-WQJvWU_HZFo-unsplash.jpg" alt="Aperçu de l'Auberge La Canebière" />
      </picture>
      <div class="lodging-card-description">
        <h3 class="is-title">Auberge La Canebière</h3>
        <p class="price-p">Nuit à partir de 25<span class="is-fw-semibold"></span></p>
        <div class="rating-container">
          <i class="fas fa-star is-checked" aria-hidden="true"></i>
          <i class="fas fa-star is-checked" aria-hidden="true"></i>
          <i class="fas fa-star is-checked" aria-hidden="true"></i>
          <i class="fas fa-star is-checked" aria-hidden="true"></i>
          <i class="fas fa-star is-unchecked" aria-hidden="true"></i>
        </div>
      </div>
    </article>
  </div>
</a>
Résultat : j’ai une page avec des images qui sont constamment en excellente qualité.
Tout en honorant un Page speed tout à fait décent.
Sans JavaScript ni intelligence d’optimisation particulière.

Concernant le CSS #

Bien qu’HTML et CSS ne soient pas considérés comme des langages de programmation, et que je ne consacrerai pas mon article de blog à répondre à ce débat : le CSS est à mon sens un langage tout à fait intéressant et sur lequel on retrouve de la technicité et de l’intelligence dans son écriture.

Notions de Monkey patching et de modularisation #

Comme on en a plutôt l’habitude en programmation : le code CSS est lu de haut en bas.

Et tout comme on a également l’habitude avec des langages de programmation dits dynamiques : il est possible de “Patcher” du code CSS en le réécrivant tout ou partie un peu plus bas que là où il a été déclaré pour la première fois.
🔗 Monkey patching
Simple rappel concernant la notion de surchage en CSS #

Commençons par quelque chose que (presque) tout le monde sait.

Cela fait partie des premières choses que l’on apprend concernant le CSS : c’est le dernier choix que vous faites en CSS qui est pris en compte.

🔗 How to Override CSS Styles

Commençons par un exemple très simple :

html p {
  color: red;
}

/* ... Monkey patch */
html p {
  color: blue;
}

Le texte des paragraphes de ma page HTML sera bleu.
On a ici « Surchargé » la propriété color du sélecteur html p pour la faire passer de la valeur red à la valeur blue.

Dans le même principe : si l’on avait d’abord chargé un premier fichier CSS qui applique cette propriété red, puis un second qui applique finalement la propriété blue, on aurait donc modifié le comportement global de notre affichage sans modifier le premier fichier.

À présent que ce rappel a été fait : que peut-on en tirer de réellement intéressant ?
Quelle intelligence peut-on y appliquer ?

Les variables en CSS #

Il est possible depuis CSS 3 d’utiliser des variables dans ses feuilles de style.

Imaginons que l’on parte sur le nuancier suivant :

Nuancier d'exemple généré avec Adobe Color

Nuancier d’exemple généré avec 🔗 Adobe Color

J’aurais donc seulement cinq couleurs à utiliser sur l’intégralité du design d’un produit :

  • A : #005270
  • B : #49C5F2
  • C : #00B0F0
  • D : #225B70
  • E : #008ABD

J’ai à présent deux possibilités :

  • Écrire partout dans mon code, en dur, #005270 dès lors que j’ai besoin de la couleur A (pas bien ! Pas bien du tout du tout !)
  • Créer une variable --primary-color et lui donner la valeur #005270, puis écrire var(--primary-color).

N’oubliez pas la notion de portée lexicale ! #
Comme dans d’autres langages de programmation, les variables du CSS sont « Scopées ».
🔗 CSS variables: Scoping

Cela permet d’appliquer une intelligence encore plus fine à son utilisation des variables.

Néanmoins, dans le cas d’un nuancier à appliquer sur l’intégralité d’un produit, c’est bien plus un handicap qu’une force. C’est pour cette raison que l’on déclarera ces variables dans le scope global, dans le pseudo-élément :root.


Le pseudo-élément :root en CSS #

Le pseudo-élément :root va nous permettre de déclarer des variables globales.
Ces variables sont utilisables dans tous les fichiers CSS d’un projet.

Dans notre cas, nous n’avons qu’un seul fichier de feuille de style : style.css.
Nos variables seront donc utilisables dans l’intégralité de ce fichier.
Gardez tout de même à l’esprit que dans le cas où vous ajouteriez de nouveaux fichiers CSS, ces variables y seraient également accessibles.

Je vais donc pouvoir utiliser ce pseudo-élément, comme un module de configuration globale :

:root {
  /* Configuration.globalLayout => font */
  --booki-font: 'Raleway';

  /* Configuration.chart => Static Colors */
  --booki-chart-primary: #0065fc;
  --booki-chart-bg-color: #f2f2f2;
  --booki-chart-sections-bg-color: white;
  --booki-chart-filter-border-color: #d9d9d9;

  /* Configuration.chart => FX Colors */
  --booki-chart-filter-btn-hover: #deebff;
  --booki-inactive-menu-elm-indicator-color: transparent;
  --booki-active-menu-elm-indicator-color: var(--booki-chart-primary);
  --booki-chart-blue-btn-hover: var(--booki-chart-primary);

  /* ... */
}

À présent, si j’écris plus bas dans mon code CSS :

html p {
  color: var(--booki-chart-primary);
}

Les paragraphes de ma page auront alors la couleur : #0065FC.

Une volonté personnelle de séparer la configuration et l’implémentation #

J’ai à présent mes variables globales dans mon pseudo-élément :root.

Je voudrais, immédiatement après décrire la configuration globale de chacun de mes éléments dans mon code CSS.

J’aurais bien aimé pouvoir séparer ces informations dans plusieurs fichiers CSS, puis jouer avec la at-rule @import et un bundler afin de pouvoir tout regrouper dans un seul fichier CSS au déploiement…

Mais ce n’était pas possible !
Les contraintes imposées sur ce projet ne me le permettaient pas.

Afin de clarifier cette volonté dans mon choix de réalisation du projet, j’ai donc décidé d’appeler cette spécificité des Tweakers dans mon code, et ajouté une classe .has-tweakers sur les éléments concernés.

Ainsi :

/* Constants: Top level wrapper width */
#top-level-wrapper.has-tweakers {
  --_width: 1400px;
}

/* Constants: Header nav's deadzone, header's height and its bottom gap */
#header.has-tweakers {
  min-height: 65px;
  margin-bottom: 55px;
}

/* Mutables: Header navigation menu's colors (NOT on mouse-over) */
.header-nav.has-tweakers {
  --_menu-active-item-hyperlink-color: inherit;
  --_menu-active-item-border-top-color: var(--booki-inactive-menu-elm-indicator-color);
}

/* Mutables: Header navigation menu's colors (WHEN on mouse-over) */
.header-nav-item.has-tweakers:hover {
  --_menu-active-item-hyperlink-color: var(--booki-chart-primary);
  --_menu-active-item-border-top-color: var(--booki-active-menu-elm-indicator-color);
  border-top-width: var(--_border-top-width);
}

/* ... */

J’ai également fait le choix d’écrire sous forme de variables certaines spécifications.
Ce qui m’a permis par exemple de calculer dynamiquement la largeur du wrapper qui englobe tout le contenu de ma page web selon la largeur spécifiée sur la maquette (1400 pixels), et ma gestion d’état du fond perdu de ma page.

/* Constants: Top level wrapper width */
#top-level-wrapper.has-tweakers {
  --_width: 1400px;
}

/* ... */

/* Webpage Wrapper */
#top-level-wrapper {
  /* ... */
  max-width: calc(var(--_width) + var(--booki-bleed-current-state-value));
  /* ... */
}

Ici, la variable --_width n’est accessible que dans le contexte de #top-level-wrapper et de ses éléments enfants (principe de scope).

J’ai à présent un code CSS qui commence par :

  • Définir des variables globales,
  • Définir des Tweakers qui permettent de m’adapter à la maquette, sans mêler le fond et la forme de mon code,
  • Créer une logique de calcul interne (la notion de gestion d’états sera expliquée plus tard).

Par la suite, mon code comportera la ligne :

/* ⛔ Do NOT edit the code BELOW this line unless you know what you are doing */

Cela signifie que je signale à mon utilisateur que les lignes ci-après du code seront plus techniques : il ne s’agit plus de Tweakers que n’importe qui pourrait venir modifier sans provoquer des comportements à la fois inattendus et difficilement compréhensibles.

J’ai donc un code qui respecte une certaine notion de séparation de configuration et d’implémentation.

Par la suite, le code deviendra assez classique pour un utilisateur habitué du CSS.

Gestion d’états #
Peut-on considérer les media queries comme des événements ?
Et peut-on commencer à se rapprocher d’une logique de gestion d’états ?
Cas d’application dans Booki #

La maquette de Booki n’était pas très précise en tout point concernant l’œil de designer qu’il fallait lui apporter.
Il était question d’une largeur maximale de 1400 pixels. Mais : aucun aperçu concret de ce que donne le site sur un écran de 1920 pixels de large. Hum…

Plus flou encore : selon que l’on regarde la maquette de la version ordinateur, tablette ou mobile, on constate que les marges sur les côtés changent.
Marges de la version ordinateur, sur la maquette de la page d'accueil de Booki

Mise en évidence des marges de la version ordinateur sur la maquette de la page d’accueil de Booki.

Marges de la version tablette, sur la maquette de la page d'accueil de Booki

Mise en évidence des marges de la version tablette sur la maquette de la page d’accueil de Booki.

Marges de la version smartphone, sur la maquette de la page d'accueil de Booki

Mise en évidence des marges de la version téléphone sur la maquette de la page d’accueil de Booki.

Ce qui était attendu était de tout simplement laisser les côtés de la page transparents.
Finalement, il aurait suffi de bêtement centrer la page et laisser un fond blanc, et l’illusion aurait été suffisante…

Mais en design, la notion de marges et de fond perdu n’est pas la même !
🔗 There Will Be Bleed (and other design terms you should know)

C’est donc pour cette raison que j’ai décidé d’avoir une distinction explicite entre mon intégration des marges et du fond perdu de la page web !

À présent, si j’applique un arrière-plan qui n’est pas blanc, je vois de façon évidente que cette spécificité a bien été comprise et appliquée.

Aperçu des marges et du fond perdu de l'intégration de la page web d'accueil de l'agence de voyage Booki, version ordinateur

Mise en évidence de la distinction entre le fond perdu et la largeur de la page dans l’intégration de la version ordinateur de la landing page de Booki.

Implémentation d’une logique de largeur dynamique et de fond perdu #

Dans mon pseudo-élément :root, je définis ma variable globale --booki-bleed-current-state-value, et je lui donne comme valeur initiale : var(--booki-bleed-desktop).

var(--booki-bleed-desktop) vient de devenir la valeur de fallback de --booki-bleed-current-state-value.

Pour rappel : nous sommes sur une intégration en Desktop First. C’est pour cette raison que c’est var(--booki-bleed-desktop) qui a été choisi.

À présent, je vais aller modifier cette variable dans mes breakpoints.

/*** D. Breakpoints */
/* ✨ [§ D.1) Bleed Manager] */
@media (max-width: 992px) {
  :root {
    --booki-bleed-current-state-value: var(--booki-bleed-tablet);
  }
}

@media (max-width: 768px) {
  :root {
    --booki-bleed-current-state-value: var(--booki-bleed-mobile);
  }

  #content > .lodgings-section,
  #content > .activities,
  #top-level-wrapper > #header,
  #top-level-wrapper > #footer {
    padding: 0;
  }
}

Et zouh ! 🎉

Aperçu des marges et du fond perdu de l'intégration de la page web d'accueil de l'agence de voyage Booki, version tablette

Mise en évidence de la distinction entre le fond perdu et la largeur de la page dans l’intégration de la landing page de Booki, version tablette.

Aperçu des marges et du fond perdu de l'intégration de la page web d'accueil de l'agence de voyage Booki, version mobile

Mise en évidence de la distinction entre le fond perdu et la largeur de la page dans l’intégration de la landing page de Booki, version téléphone.

.has-tweakers et le retour de bâton des spécificités de sélecteurs en CSS #

Imaginons que j’écrive :

.filter-button.has-tweakers {
  /* ... */
  padding: 0.9rem 1.2rem 0.9rem 0.9rem;
  /* ... */
}

Puis que je décide de monkey patch la valeur de cette propriété padding plus bas dans mon code…

@media (min-width: 366px) and (max-width: 495px) {
  .filter-button {
    padding: 0.5rem 0.8rem 0.5rem 0.5rem;
  }
}

Et bien : ça ne fonctionne pas !
padding reste à une valeur de .9rem 1.2rem .9rem .9rem.

Nous pouvons retrouver l’origine de ce problème grâce à cet outil.
🔗 Calculer la spécificité de ses sélecteurs CSS
Mise en évidence d'une différence de score de spécificité entre deux sélecteurs CSS

.filter-button.has-tweakers obtient un meilleur score de spécificité que .filter-button, car il chaîne deux noms de classes CSS.

Cela signifie que les propriétés que l’on écrit dans .filter-button.has-tweakers sont prioritaires sur celles que l’on écrit dans .filter-button !
Il s’agit d’une particularité dans la logique de Monkey Patch de CSS dont je n’avais pas parlé jusqu’à présent.

Aïe…
C’est très embêtant.

Mais alors que faire ?
Également écrire .filter-button.has-tweakers dans mes media queries ?

Je trouve ce choix très ennuyeux, car j’aimerais que lorsque je navigue de .has-tweakers en .has-tweakers dans mon fichier CSS à l’aide du raccourci clavier CTRL+F, je ne puisse pas brutalement changer de contexte de configuration sans m’en rendre compte.

J’ai donc à la place fait le choix de créer une nouvelle classe, .enable-tweakers-bypass.

Ainsi, je peux à présent patcher en contournant mon tweaker initial, de façon explicite dans mon code :

@media (min-width: 366px) and (max-width: 495px) {
  .filter-button.enable-tweakers-bypass {
    padding: 0.5rem 0.8rem 0.5rem 0.5rem;
  }
}

Cependant, cela induit de devoir polluer mon code HTML avec une classe supplémentaire.

Dans un projet dépourvu des contraintes initiales évoquées, nous aurions pu éviter ces casse-têtes et simplement passer par un bundler CSS afin de pouvoir être tout aussi modulaire, mais sans devoir induire un code aussi inutilement sophistiqué.
 
Si vous n’êtes pas sûr d’avoir bien compris la notion de spécificité en CSS, cette vidéo de Grafikart vous l’expliquera à merveille.


Bravo ! 🎉
Vous avez terminé la lecture de cet article !

Si ce n’est pas déjà fait, vous pouvez voir le résultat en lançant la démo de ce projet.

Cliquez sur le bouton ci-dessous pour découvrir la page web que j’ai réalisée et accédez-y dès maintenant !

  👉 Lancer la démo !

Merci de m'avoir lu.


Annexes #

Liens externes #

Si vous êtes dév : il y a aussi quelques liens en bonus pour vous.

Captures d’écran #

Capture d'écran de la version ordinateur de la page d'accueil de Booki

Capture d’écran de la version ordinateur de la page d’accueil de Booki.

Capture d'écran de la version tablette de la page d'accueil de Booki

Capture d’écran de la version tablette de la page d’accueil de Booki.

Capture d'écran de la version mobile de la page d'accueil de Booki (1/4)Capture d'écran de la version mobile de la page d'accueil de Booki (2/4)Capture d'écran de la version mobile de la page d'accueil de Booki (3/4)Capture d'écran de la version mobile de la page d'accueil de Booki (4/4)

Captures d'écran de la version mobile de la page d'accueil de Booki que j'ai réalisée.


Si ce n’est pas déjà fait, vous pouvez voir le résultat en lançant la démo de ce projet.

Cliquez sur le bouton ci-dessous pour découvrir la page web que j’ai réalisée et accédez-y dès maintenant !

  👉 Lancer la démo !


Mes projets réalisés en formation chez OpenClassrooms - Cet article fait partie d'une série.
Partie 1: Cet article