🎨 Feature: Configuration Tailwind + Templates PHP + Page Canary

Stories 1.2, 1.3, 1.4:
- Tailwind CSS configuré avec palette sombre personnalisée
- Templates header.php et footer.php avec meta SEO/Open Graph
- Fonction include_template() pour les composants réutilisables
- Page canary avec animations et composants de test
- Configuration nginx exemple pour le déploiement
- Dépendances: vlucas/phpdotenv, tailwindcss, postcss, autoprefixer

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-22 22:42:21 +01:00
parent e2cdf70f66
commit 5b10afb6db
12 changed files with 2066 additions and 36 deletions

199
assets/css/input.css Normal file
View File

@@ -0,0 +1,199 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
html {
@apply scroll-smooth;
}
body {
@apply bg-background text-text-primary font-sans antialiased;
}
h1 { @apply text-display text-text-primary; }
h2 { @apply text-heading text-text-primary; }
h3 { @apply text-subheading text-text-primary; }
p { @apply text-body text-text-secondary; }
a {
@apply text-primary hover:text-primary-light transition-colors duration-150;
}
:focus-visible {
@apply outline-none ring-2 ring-primary ring-offset-2 ring-offset-background;
}
::selection {
@apply bg-primary/30 text-text-primary;
}
}
@layer components {
/* Container */
.container-content {
@apply max-w-content mx-auto px-4 sm:px-6 lg:px-8;
}
/* Boutons */
.btn {
@apply inline-flex items-center justify-center gap-2
px-6 py-3 font-medium rounded-lg
transition-all duration-150
focus:outline-none focus:ring-2 focus:ring-offset-2
focus:ring-offset-background
disabled:opacity-50 disabled:cursor-not-allowed;
}
.btn-primary {
@apply btn bg-primary text-background
hover:bg-primary-light active:bg-primary-dark
focus:ring-primary;
}
.btn-secondary {
@apply btn border-2 border-primary text-primary bg-transparent
hover:bg-primary hover:text-background
focus:ring-primary;
}
.btn-ghost {
@apply btn text-primary bg-transparent
hover:text-primary-light hover:bg-surface-light
focus:ring-primary;
}
/* Badges */
.badge {
@apply inline-flex items-center px-2.5 py-1
text-xs font-medium rounded
bg-surface-light text-text-secondary;
}
.badge-primary {
@apply bg-primary/20 text-primary;
}
.badge-muted {
@apply bg-border text-text-muted;
}
/* Cartes */
.card {
@apply bg-surface rounded-lg overflow-hidden
border border-border/50
transition-all duration-200;
}
.card-interactive {
@apply card cursor-pointer
hover:-translate-y-1 hover:shadow-card-hover
hover:border-border;
}
.card-body {
@apply p-4 sm:p-6;
}
/* Inputs */
.input {
@apply w-full px-4 py-3
bg-surface border border-border rounded-lg
text-text-primary placeholder-text-muted
transition-all duration-150
focus:outline-none focus:border-primary focus:shadow-input-focus;
}
.input-error {
@apply border-error focus:border-error
focus:shadow-[0_0_0_3px_rgba(248,113,113,0.2)];
}
.textarea {
@apply input min-h-[150px] resize-y;
}
.label {
@apply block text-sm font-medium text-text-secondary mb-2;
}
.label-required::after {
content: '*';
@apply text-error ml-1;
}
.error-message {
@apply text-sm text-error mt-1.5 flex items-center gap-1;
}
/* Sections */
.section {
@apply py-16 sm:py-24;
}
.section-header {
@apply text-center mb-12;
}
.section-title {
@apply text-heading mb-4;
}
.section-subtitle {
@apply text-body text-text-secondary max-w-2xl mx-auto;
}
/* Témoignage */
.testimonial {
@apply bg-surface-light rounded-lg p-6 border-l-4 border-primary;
}
/* Breadcrumb */
.breadcrumb {
@apply flex items-center gap-2 text-sm text-text-muted;
}
.breadcrumb-link {
@apply text-text-secondary hover:text-primary transition-colors;
}
.breadcrumb-current {
@apply text-text-primary;
}
}
@layer utilities {
.animate-fade-in {
animation: fadeIn 0.6s ease-out forwards;
}
.animate-fade-in-up {
animation: fadeInUp 0.6s ease-out forwards;
}
.animation-delay-100 { animation-delay: 100ms; }
.animation-delay-200 { animation-delay: 200ms; }
.animation-delay-300 { animation-delay: 300ms; }
.aspect-thumbnail {
aspect-ratio: 16 / 9;
}
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* Accessibilité - Réduction de mouvement */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}

6
assets/js/main.js Normal file
View File

@@ -0,0 +1,6 @@
// assets/js/main.js
// Script principal du portfolio
document.addEventListener('DOMContentLoaded', () => {
console.log('Portfolio chargé');
});

494
composer.lock generated Normal file
View File

@@ -0,0 +1,494 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "adbddd7a48b14ed78896b2d6c5ef28e9",
"packages": [
{
"name": "graham-campbell/result-type",
"version": "v1.1.4",
"source": {
"type": "git",
"url": "https://github.com/GrahamCampbell/Result-Type.git",
"reference": "e01f4a821471308ba86aa202fed6698b6b695e3b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b",
"reference": "e01f4a821471308ba86aa202fed6698b6b695e3b",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9.5"
},
"require-dev": {
"phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7"
},
"type": "library",
"autoload": {
"psr-4": {
"GrahamCampbell\\ResultType\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "An Implementation Of The Result Type",
"keywords": [
"Graham Campbell",
"GrahamCampbell",
"Result Type",
"Result-Type",
"result"
],
"support": {
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
"type": "tidelift"
}
],
"time": "2025-12-27T19:43:20+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.9.5",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
"reference": "75365b91986c2405cf5e1e012c5595cd487a98be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be",
"reference": "75365b91986c2405cf5e1e012c5595cd487a98be",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
},
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"autoload": {
"psr-4": {
"PhpOption\\": "src/PhpOption/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh"
},
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "Option Type for PHP",
"keywords": [
"language",
"option",
"php",
"type"
],
"support": {
"issues": "https://github.com/schmittjoh/php-option/issues",
"source": "https://github.com/schmittjoh/php-option/tree/1.9.5"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
"type": "tidelift"
}
],
"time": "2025-12-27T19:41:33+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": ">=7.2"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-12-23T08:48:59+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php80\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ion Bazan",
"email": "ion.bazan@gmail.com"
},
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-01-02T08:10:11+00:00"
},
{
"name": "vlucas/phpdotenv",
"version": "v5.6.3",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
"reference": "955e7815d677a3eaa7075231212f2110983adecc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc",
"reference": "955e7815d677a3eaa7075231212f2110983adecc",
"shasum": ""
},
"require": {
"ext-pcre": "*",
"graham-campbell/result-type": "^1.1.4",
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9.5",
"symfony/polyfill-ctype": "^1.26",
"symfony/polyfill-mbstring": "^1.26",
"symfony/polyfill-php80": "^1.26"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"ext-filter": "*",
"phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
},
"suggest": {
"ext-filter": "Required to use the boolean validator."
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
},
"branch-alias": {
"dev-master": "5.6-dev"
}
},
"autoload": {
"psr-4": {
"Dotenv\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Vance Lucas",
"email": "vance@vancelucas.com",
"homepage": "https://github.com/vlucas"
}
],
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
"keywords": [
"dotenv",
"env",
"environment"
],
"support": {
"issues": "https://github.com/vlucas/phpdotenv/issues",
"source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
"type": "tidelift"
}
],
"time": "2025-12-27T19:49:13+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=8.0"
},
"platform-dev": {},
"plugin-api-version": "2.6.0"
}

15
includes/functions.php Normal file
View File

@@ -0,0 +1,15 @@
<?php
/**
* Fonctions helpers du portfolio
*/
/**
* Inclut un template avec des données
* @param string $name Nom du template (sans .php)
* @param array $data Variables à passer au template
*/
function include_template(string $name, array $data = []): void
{
extract($data);
include __DIR__ . "/../templates/{$name}.php";
}

View File

@@ -1,11 +1,53 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Portfolio - Skycel</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
<?php
// index.php - Page Canary
require_once __DIR__ . '/includes/functions.php';
include_template('header', [
'pageTitle' => 'Portfolio en construction',
'pageDescription' => 'Mon portfolio de développeur web arrive bientôt. Restez connectés !'
]);
?>
<main class="min-h-screen flex items-center justify-center">
<div class="container-content text-center py-20">
<!-- Titre principal -->
<h1 class="text-display text-text-primary mb-4 animate-fade-in">
Portfolio <span class="text-primary">en construction</span>
</h1>
<!-- Sous-titre -->
<p class="text-xl text-text-secondary mb-8 max-w-2xl mx-auto animate-fade-in animation-delay-100">
Je prépare quelque chose de génial pour vous.
<br>Revenez bientôt pour découvrir mes projets !
</p>
<!-- Badge de test -->
<div class="flex justify-center gap-4 mb-12 animate-fade-in animation-delay-200">
<span class="badge">PHP</span>
<span class="badge">Tailwind CSS</span>
<span class="badge badge-primary">En cours</span>
</div>
<!-- Card de test -->
<div class="card max-w-md mx-auto animate-fade-in animation-delay-300">
<div class="card-body">
<h3 class="text-subheading mb-2">Infrastructure validée</h3>
<p class="text-text-secondary mb-4">
PHP, Tailwind CSS et le serveur fonctionnent correctement.
</p>
<div class="flex gap-4 justify-center">
<span class="btn-primary">Bouton Primary</span>
<span class="btn-secondary">Bouton Secondary</span>
</div>
</div>
</div>
<!-- Test responsive -->
<p class="text-text-muted text-sm mt-12">
Testé sur mobile, tablette et desktop.
</p>
</div>
</main>
<?php include_template('footer'); ?>

61
nginx.conf.example Normal file
View File

@@ -0,0 +1,61 @@
# Configuration Nginx pour le Portfolio
# À adapter selon votre serveur
server {
listen 80;
server_name monportfolio.fr www.monportfolio.fr;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name monportfolio.fr www.monportfolio.fr;
# SSL - À adapter selon votre configuration Let's Encrypt
ssl_certificate /etc/letsencrypt/live/monportfolio.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/monportfolio.fr/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/portfolio;
index index.php;
charset utf-8;
# Headers de sécurité
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Bloquer fichiers sensibles
location ~ /\.(env|git|htaccess) { deny all; return 404; }
location ^~ /vendor/ { deny all; return 404; }
location ^~ /node_modules/ { deny all; return 404; }
location ^~ /logs/ { deny all; return 404; }
location ^~ /data/ { deny all; return 404; }
location ^~ /includes/ { deny all; return 404; }
# Assets statiques avec cache long
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
gzip_static on;
}
# Router PHP (front controller)
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP-FPM - À adapter selon votre version PHP
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/javascript application/javascript application/json image/svg+xml;
}

1110
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +1,24 @@
{
"name": "portfolio-v0.1_scss",
"version": "0.1.0",
"description": "Skycel's portfolio",
"main": "index.js",
"name": "skycel-portfolio",
"version": "2.0.0",
"description": "Portfolio développeur web - Skycel",
"author": "Skycel9",
"license": "ISC",
"scripts": {
"compile": "sass ./public/assets/scss/style.scss ./public/assets/css/style.css --no-source-map",
"watch": "sass ./public/assets/scss/style.scss ./public/assets/css/style.css -watch --no-source-map",
"build": "sass ./public/assets/css/style.css ./public/assets/css/style.min.css --style=compressed --no-source-map"
"dev": "tailwindcss -i ./assets/css/input.css -o ./assets/css/output.css --watch",
"build": "tailwindcss -i ./assets/css/input.css -o ./assets/css/output.css --minify"
},
"devDependencies": {
"tailwindcss": "^3.4.0",
"postcss": "^8.4.0",
"autoprefixer": "^10.4.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Skycel9/Portfolio.git"
},
"keywords": [
"portfolio",
"development",
"Skycel9"
],
"author": "Skycel9",
"license": "ISC",
"bugs": {
"url": "https://github.com/Skycel9/Portfolio/issues"
},
"homepage": "https://github.com/Skycel9/Portfolio",
"dependencies": {},
"prettier": {
"ignore": [
"**/*.min.js",
"**/*.min.css"
]
}
"homepage": "https://github.com/Skycel9/Portfolio"
}

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}

55
tailwind.config.js Normal file
View File

@@ -0,0 +1,55 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./*.php',
'./pages/**/*.php',
'./templates/**/*.php',
'./assets/js/**/*.js'
],
theme: {
extend: {
colors: {
primary: {
DEFAULT: '#FA784F',
light: '#FB9570',
dark: '#E5623A',
},
background: '#17171F',
surface: {
DEFAULT: '#1E1E28',
light: '#2A2A36',
},
border: '#3A3A48',
text: {
primary: '#F5F5F7',
secondary: '#A1A1AA',
muted: '#71717A',
},
success: '#34D399',
warning: '#FBBF24',
error: '#F87171',
info: '#60A5FA',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
fontSize: {
'display': ['2.5rem', { lineHeight: '1.2', fontWeight: '700' }],
'heading': ['2rem', { lineHeight: '1.3', fontWeight: '600' }],
'subheading': ['1.5rem', { lineHeight: '1.4', fontWeight: '600' }],
'body': ['1rem', { lineHeight: '1.6', fontWeight: '400' }],
'small': ['0.875rem', { lineHeight: '1.5', fontWeight: '400' }],
},
maxWidth: {
'content': '1280px',
},
boxShadow: {
'card': '0 4px 20px rgba(0, 0, 0, 0.25)',
'card-hover': '0 10px 40px rgba(0, 0, 0, 0.3)',
'input-focus': '0 0 0 3px rgba(250, 120, 79, 0.2)',
},
},
},
plugins: [],
}

19
templates/footer.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
/**
* Template Footer
*/
$currentYear = date('Y');
?>
<!-- Footer -->
<footer class="bg-surface border-t border-border py-8 mt-auto">
<div class="container-content text-center">
<p class="text-text-muted text-sm">
&copy; <?= $currentYear ?> Portfolio. Tous droits réservés.
</p>
</div>
</footer>
<!-- Scripts -->
<script src="/assets/js/main.js" defer></script>
</body>
</html>

38
templates/header.php Normal file
View File

@@ -0,0 +1,38 @@
<?php
/**
* Template Header
* Variables disponibles :
* - $pageTitle (string) : Titre de la page
* - $pageDescription (string, optionnel) : Meta description
*/
$pageTitle = $pageTitle ?? 'Portfolio - Développeur Web';
$pageDescription = $pageDescription ?? 'Portfolio de développeur web full-stack. Découvrez mes projets, compétences et parcours.';
$siteName = 'Portfolio';
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="<?= htmlspecialchars($pageDescription, ENT_QUOTES, 'UTF-8') ?>">
<!-- Open Graph -->
<meta property="og:title" content="<?= htmlspecialchars($pageTitle, ENT_QUOTES, 'UTF-8') ?>">
<meta property="og:description" content="<?= htmlspecialchars($pageDescription, ENT_QUOTES, 'UTF-8') ?>">
<meta property="og:type" content="website">
<meta property="og:locale" content="fr_FR">
<!-- Preload fonts -->
<link rel="preload" href="/assets/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/jetbrains-mono-var.woff2" as="font" type="font/woff2" crossorigin>
<!-- Favicon -->
<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
<!-- CSS -->
<link rel="stylesheet" href="/assets/css/output.css">
<title><?= htmlspecialchars($pageTitle, ENT_QUOTES, 'UTF-8') ?> | <?= $siteName ?></title>
</head>
<body class="bg-background text-text-primary font-sans antialiased">