🇫🇷 Français · English
Ce repo contient ma solution au défi Four card feature section de Frontend Mentor.
🎯 Objectif personnel : apprendre à maîtriser Tailwind V4 pour composer une grille responsive propre (mobile → tablette → desktop).
- Reproduire la section “Four card feature” sur mobile et desktop.
npm install
npm run dev # serveur local
npm run build # build de production (dossier dist/)
npm run preview # prévisualisation du build- HTML5 sémantique (
main,section,header,footer,article+aria-labelledby) - Tailwind CSS v4 (approche CSS-first avec
@themepour définir des tokens) - Mobile-first workflow
- Polices locales WOFF2 Poppins 200/400/600 +
font-display: swap+preload. - Vite (build performant, purge console/debugger, assets fingerprintés)
- Netlify (headers de sécurité & cache via
netlify.toml)
- Un
<h1>masqué visuellement sert de titre de page et labellise la section viaaria-labelledby. - Chaque carte est un
<article>avec son propreh3. - Les icônes sont strictement décoratives →
alt=""+aria-hidden="true"et dimensions explicites (width/height) pour éviter le CLS.
Extrait :
<h1 class="sr-only" id="page-title">Four card feature section</h1>
<section class="mx-auto" aria-labelledby="page-title">
<article
class="card top-border accent-cyan area-super shadow-mix rounded-md p-8"
aria-labelledby="feat-supervisor"
>
<header>
<h3 class="text-grey-500 mb-1 text-lg font-semibold" id="feat-supervisor">
Supervisor
</h3>
</header>
<p class="text-sm/relaxed">
Monitors activity to identify project roadblocks
</p>
<footer>
<img
class="mt-8 mb-4 ml-auto"
src="/assets/icon-supervisor.svg"
width="64"
height="64"
aria-hidden="true"
alt=""
/>
</footer>
</article>
</section>Tokens de couleurs & fonts via @theme ; accent bleu exposé en HSL (composantes séparées) pour réutiliser facilement la teinte dans les ombres.
Extrait :
@theme {
--color-red: hsl(0, 78%, 62%);
--color-cyan: hsl(180, 62%, 55%);
--color-orange: hsl(34, 97%, 64%);
--color-blue: hsl(212, 86%, 64%);
--color-grey-500: hsl(234, 12%, 34%);
--color-grey-400: hsl(212, 6%, 44%);
--color-accent: 212 86% 64%; /* HSL parts for alpha mixing */
--font-sans: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--font-poppins: "Poppins", var(--font-sans);
}Empilement mobile par défaut ; tablette en 2 colonnes (super/karma puis team/calc) ; desktop en 3 colonnes avec Supervisor et Calculator qui encadrent (span vertical) et Team/Karma au centre.
J'ai d'abord pensé à wrapper ensemble le deuxième et troisième articles, et de les intégrer dans une grille à trois colonnes sur desktop, mais le design manquait de responsivité sur tablette (il fallait une version à deux colonnes). Pour y parvenir, j'ai opté pour la solution offerte par
grid-template-areasafin de maîtriser totalement l'emplacement des cards dans la grille.
Extrait :
@layer components {
.grid-cards {
display: grid;
gap: 1.5rem;
align-items: center;
justify-items: center;
/* Tablet */
@media (min-width: 48rem) {
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-areas:
"super karma"
"team calc";
}
/* Desktop */
@media (min-width: 68rem) {
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-areas:
"super team calc"
"super karma calc";
}
}
/* Déclarés seulement à partir de 48rem, quand le template en a besoin, pour éviter l'empilement sur mobile */
.area-super {
@media (min-width: 48rem) {
grid-area: super;
}
}
.area-team {
@media (min-width: 48rem) {
grid-area: team;
}
}
.area-karma {
@media (min-width: 48rem) {
grid-area: karma;
}
}
.area-calc {
@media (min-width: 48rem) {
grid-area: calc;
}
}
}J’ai d’abord testé une border-top: 4px directement sur la card ; mais avec les coins arrondis, la bordure suivait la courbure latérale et ne rendait pas l’effet souhaité. Pour donner l’illusion d’un liseré qui s’insère dans la card, j’ai ajouté un pseudo-élément sur chaque card, avec ses propres rayons.
Extrait :
.top-border {
position: relative;
}
.top-border::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 4px;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
}
/* Couleurs par accent */
.accent-cyan::before {
background-color: var(--color-cyan);
}
.accent-red::before {
background-color: var(--color-red);
}
.accent-orange::before {
background-color: var(--color-orange);
}
.accent-blue::before {
background-color: var(--color-blue);
}Avantage : on garde les coins externes de la card propres, tout en contrôlant exactement la forme et la couleur du liseré supérieur.
Pour retrouver la légère teinte bleue du design tout en conservant une ombre douce et moderne, j’empile plusieurs couches : deux grises + une bleutée (HSL avec alpha). Cela évite une ombre trop “colorée” tout en restant cohérent avec la palette.
Extrait :
@layer components {
.shadow-mix {
box-shadow:
0 20px 30px -12px rgba(0, 0, 0, 0.1),
0 12px 18px -10px rgba(0, 0, 0, 0.02),
0 18px 28px -14px hsl(var(--color-accent) / 0.3);
transition: box-shadow 0.25s ease-out /* + transform si souhaité */;
}
@media (hover: hover) and (pointer: fine) {
.shadow-mix:hover {
box-shadow:
0 26px 36px -13px rgba(0, 0, 0, 0.1),
0 18px 26px -11px rgba(0, 0, 0, 0.02),
0 26px 36px -15px hsl(var(--color-accent) / 0.3);
/* optionnel : transform: translateY(-2px); */
}
}
}Remarque : si vous observez un léger flou du texte au survol sous Windows/Chrome, évitez
transformsur l’élément contenant du texte et animez uniquement l’ombre, ou appliquez le transform sur un pseudo-élément.
- Hiérarchie logique :
h1(SR-only) →h2introductif →h3par carte. - Association section ↔ titre via
aria-labelledby. - Icônes décoratives masquées (
alt="",aria-hidden="true"). - Dimensions d’images explicites (
width/height) pour la stabilité du layout.
- Tailwind v4 JIT → CSS minimal.
- Polices WOFF2 +
preloadciblé +swap. - Vite :
sourcemap: false,drop: ["console","debugger"],assetsInlineLimit: 0pour éviter l’inlining d’assets. - Netlify : cache long sur assets fingerprintés/polices, en-têtes de sécurité pour la version en prod publiée pour la preview.
- Semantic HTML5 markup + ARIA minimale.
- CSS custom properties (via tokens Tailwind v4 dans
@themeet@layer componentspour des utilitaires maison (grille, accents, ombres, liseré supérieur)) - Mobile-first workflow
- Vite (bundler/build)
- Tailwind CSS v4
- Netlify (hébergement & headers)
- Tailwind v4 –
@theme& tokens : https://tailwindcss.com/docs/theme
- Website (preview) — https://www.julienborgeon.fr
Merci à Frontend Mentor pour le design du challenge et à la communauté pour les retours.
Un clin d’œil aux ressources MDN & Tailwind pour la clarté de leur doc.
└── 📁fem-four-card-feature-section
└── 📁.vscode
├── settings.json
└── 📁public
└── 📁assets
├── favicon-32x32.png
├── icon-calculator.svg
├── icon-karma.svg
├── icon-supervisor.svg
├── icon-team-builder.svg
└── 📁fonts
├── poppins-v23-latin-200.woff2
├── poppins-v23-latin-600.woff2
├── poppins-v23-latin-regular.woff2
├── og.jpg
└── 📁src
├── main.css
├── .gitattributes
├── .gitignore
├── .prettierrc
├── index.html
├── netlify.toml
├── package-lock.json
├── package.json
├── README.en.md
├── README.md
└── vite.config.js