diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/admin.py b/core/admin.py new file mode 100644 index 0000000..7f53918 --- /dev/null +++ b/core/admin.py @@ -0,0 +1,26 @@ +from django.contrib import admin +from .models import SiteSettings + +@admin.register(SiteSettings) +class SiteSettingsAdmin(admin.ModelAdmin): + # On empêche d'ajouter une nouvelle config s'il en existe déjà une + def has_add_permission(self, request): + return not SiteSettings.objects.exists() + + # On empêche de supprimer la config (trop dangereux) + def has_delete_permission(self, request, obj=None): + return False + + # Petite astuce visuelle pour l'admin + fieldsets = ( + ('Général', { + 'fields': ('site_name', 'site_logo') + }), + ('Réseaux Sociaux', { + 'fields': ('facebook_url', 'twitter_url', 'youtube_url'), + 'classes': ('collapse',) # Cache cette section par défaut pour alléger + }), + ('Contact', { + 'fields': ('contact_email',) + }), + ) \ No newline at end of file diff --git a/core/apps.py b/core/apps.py new file mode 100644 index 0000000..26f78a8 --- /dev/null +++ b/core/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CoreConfig(AppConfig): + name = 'core' diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..7d094d9 --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 6.0 on 2025-12-10 18:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='SiteSettings', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('site_name', models.CharField(default='Mon Super Site', max_length=200)), + ('site_logo', models.ImageField(blank=True, upload_to='settings/')), + ('contact_email', models.EmailField(blank=True, max_length=254)), + ('facebook_url', models.URLField(blank=True)), + ('twitter_url', models.URLField(blank=True)), + ('youtube_url', models.URLField(blank=True)), + ('instagram_url', models.URLField(blank=True)), + ('linkedin_url', models.URLField(blank=True)), + ('github_url', models.URLField(blank=True)), + ], + options={ + 'verbose_name': 'Réglages du site', + 'verbose_name_plural': 'Réglages du site', + }, + ), + ] diff --git a/core/migrations/__init__.py b/core/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/models.py b/core/models.py new file mode 100644 index 0000000..de47391 --- /dev/null +++ b/core/models.py @@ -0,0 +1,29 @@ +from django.db import models + +class SiteSettings(models.Model): + site_name = models.CharField(max_length=200, default="Mon Super Site") + site_logo = models.ImageField(upload_to='settings/', blank=True) + contact_email = models.EmailField(blank=True) + + # Réseaux sociaux + facebook_url = models.URLField(blank=True) + twitter_url = models.URLField(blank=True) + youtube_url = models.URLField(blank=True) + instagram_url = models.URLField(blank=True) + linkedin_url = models.URLField(blank=True) + github_url = models.URLField(blank=True) + + # L'astuce pour qu'il n'y ait qu'un seul réglage + def save(self, *args, **kwargs): + self.pk = 1 # On force l'ID à 1. Si tu sauvegardes, ça écrase l'existant. + super(SiteSettings, self).save(*args, **kwargs) + + def delete(self, *args, **kwargs): + pass # On empêche la suppression. On ne peut pas supprimer les réglages. + + def __str__(self): + return "Configuration Générale" + + class Meta: + verbose_name = "Réglages du site" + verbose_name_plural = "Réglages du site" \ No newline at end of file diff --git a/core/tests.py b/core/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/core/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/core/views.py b/core/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/core/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/courses/migrations/0001_initial.py b/courses/migrations/0001_initial.py new file mode 100644 index 0000000..1d53d78 --- /dev/null +++ b/courses/migrations/0001_initial.py @@ -0,0 +1,59 @@ +# Generated by Django 6.0 on 2025-12-10 18:04 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Course', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('slug', models.SlugField(unique=True)), + ('tags', models.CharField(max_length=200)), + ('thumbnail', models.ImageField(default='default.jpg', upload_to='thumbnails/courses/')), + ('description', models.TextField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('enable', models.BooleanField(default=True)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Module', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('slug', models.SlugField()), + ('description', models.TextField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('enable', models.BooleanField(default=True)), + ('order', models.PositiveIntegerField()), + ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='modules', to='courses.course')), + ], + ), + migrations.CreateModel( + name='Lesson', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('slug', models.SlugField()), + ('content', models.TextField()), + ('video_id', models.CharField(blank=True, max_length=200)), + ('is_premium', models.BooleanField(default=False)), + ('order', models.PositiveIntegerField()), + ('module', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lessons', to='courses.module')), + ], + ), + ] diff --git a/data78HyT4mloSq_Gt.sqlite3 b/data78HyT4mloSq_Gt.sqlite3 new file mode 100644 index 0000000..a94802b Binary files /dev/null and b/data78HyT4mloSq_Gt.sqlite3 differ diff --git a/static/css/app.css b/static/css/app.css new file mode 100644 index 0000000..e57ee08 --- /dev/null +++ b/static/css/app.css @@ -0,0 +1,1670 @@ +/* Design tokens — 2025 foundation */ + +/* Dark as default (overridden by light theme stylesheet) */ +:root, +[data-theme='dark'] { + /* Palette — Bleu pétrole */ + --bg: #0f1f2e; + --fg: #c4d7e0; + --muted: #5e88a0; + --primary: #4e9ed6; + --primary-contrast: #06121b; + --accent: #ffa500; + --border: #4f6f8f; + --card: #13293d; + --elev: rgba(15,31,46,0.6); + --nav-bg: rgba(0,8,36,0.65); + + /* Semantic text/link aliases */ + --text: var(--fg); + --text-muted: var(--muted); + --link: var(--primary); + --link-hover: var(--accent); + + /* Functional colors */ + --success: #2fa86c; + --success-contrast: #ffffff; + --warning: #f2a93b; + --warning-contrast: #0b0b0b; + --danger: #e35d57; + --danger-contrast: #ffffff; + --info: #5aa7e1; + --info-contrast: #0b0b0b; + + /* Neutral scale (tailored for dark UI) */ + --neutral-0: #0b1621; + --neutral-100: #0f1f2e; /* = bg */ + --neutral-200: #122638; + --neutral-300: #173145; + --neutral-400: #1d3c53; + --neutral-500: #274a62; + --neutral-600: #355c75; + --neutral-700: #4c7590; + --neutral-800: #7da0b6; + --neutral-900: #c4d7e0; /* = fg */ + + /* Surfaces & borders */ + --surface: var(--card); + --surface-raised: var(--elev); + --border-subtle: rgba(79,111,143,0.5); + --border-strong: #6a8cb0; + + /* Spacing scale */ + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 24px; + --space-6: 32px; + + /* Radius */ + --r-1: 4px; + --r-2: 6px; + --r-3: 10px; + --r-4: 16px; + + /* Shadows */ + --shadow-1: 0 1px 2px rgba(0,0,0,0.15); + --shadow-2: 0 6px 20px rgba(0,0,0,0.25); + --shadow-3: 0 12px 30px rgba(0,0,0,0.35); + + /* Transitions */ + --ease-1: cubic-bezier(.2,.8,.2,1); + --ease-2: cubic-bezier(.16,1,.3,1); + --transition-1: 200ms var(--ease-1); + --transition-2: 320ms var(--ease-2); + + /* Typography */ + --font-sans: "Montserrat", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, "Apple Color Emoji", "Segoe UI Emoji"; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + + /* Layout */ + --container-w: 1100px; + --gutter: 20px; + --focus-ring: 0 0 0 3px rgba(78,158,214,0.45); +} + +/* Light theme values — applied via colors_light.css inclusion */ +[data-theme='light'] { + /* Palette: plus nuancé, moins "blanc" */ + --bg: #eef3f7; /* fond légèrement teinté bleu-gris */ + --fg: #0f1f2e; + --muted: #385a72; /* un peu plus contrasté pour la lisibilité */ + --primary: #2f8bd6; /* bleu plus saturé pour des boutons plus colorés */ + --primary-contrast: #ffffff; + --accent: #ff7a1c; /* orange plus vif */ + --border: #bfd0dc; + --card: #ffffff; + --elev: rgba(255,255,255,0.7); + --nav-bg: rgba(255,255,255,0.85); /* barre plus lisible et moins fade */ + + --focus-ring: 0 0 0 3px rgba(47,139,214,0.30); + + /* Semantic text/link aliases */ + --text: var(--fg); + --text-muted: var(--muted); + --link: var(--primary); + --link-hover: var(--accent); + + /* Functional colors */ + --success: #1f9d63; + --success-contrast: #ffffff; + --warning: #e6951a; + --warning-contrast: #0b0b0b; + --danger: #d64541; + --danger-contrast: #ffffff; + --info: #318bd0; + --info-contrast: #ffffff; + + /* Neutral scale — davantage d'écarts entre 200/300 */ + --neutral-0: #ffffff; + --neutral-100: #f3f7fa; /* = proche du bg mais différent */ + --neutral-200: #e9f1f6; /* plus marqué qu'avant */ + --neutral-300: #dde8f0; /* idem */ + --neutral-400: #d0dde8; + --neutral-500: #bfd0dc; /* = border */ + --neutral-600: #a0bccd; + --neutral-700: #7ea0b6; + --neutral-800: #5b778a; + --neutral-900: #0f1f2e; /* = fg */ + + /* Surfaces & borders */ + --surface: var(--card); + --surface-raised: var(--elev); + --border-subtle: rgba(12,32,55,0.10); + --border-strong: #8fb0c4; +} + +html { + display: flex; + flex-direction: column; + font-size: 16px; + font-family: var(--font-sans, "Montserrat"); + scroll-behavior: smooth; +} + +body { + display: flex; + flex-direction: column; + margin: 0; + background: var(--bg); + color: var(--fg); +} + +/* Header / Navigation v2 */ +.site-nav { + position: sticky; + top: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: space-between; + height: 64px; + padding: 0 var(--gutter); + background: var(--nav-bg); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); + box-shadow: var(--shadow-1); +} + +.navbar { + display: flex; + width: 70%; + justify-content: flex-start; + margin-right: 30px; +} + +.navbar ul, .navbar li { + list-style: none; + padding: 0; + margin: 0; +} + +.navbar ul { + display: flex; + flex-direction: row; + width: 100%; +} + +.navbar li { + position: relative; + margin: auto 10px; +} + +.navend { + margin-right: 20px; + display: flex; + width: 100%; + align-items: end; + flex-direction: row-reverse; +} + +.navend a { + cursor: pointer; +} + +.navbar ul ul { + display: none; + position: absolute; + width: auto; + left: 0; + top: 100%; + flex-direction: column; + transform: translateX(-25%); + background: var(--elev); + backdrop-filter: blur(6px); + border: 1px solid var(--border); + border-radius: var(--r-2); +} + +.navbar li:hover ul, +.navbar li ul:hover { + display: flex; /* Changed to flex to support horizontal layout */ +} + +/* Accessibilité: ouvrir le sous-menu au focus clavier */ +.navbar li:focus-within > ul { + display: flex; +} + +.navbar ul ul li { + width: auto; /* Adjust width for inline layout */ + margin: auto 10px; + padding: 10px; + border-left: 1px solid var(--border); +} + +.navbar ul ul li:first-child { + margin-top: 20px; +} + +.navbar ul ul a { + display: block; + padding: 0 0 10px 0; + text-decoration: none; + white-space: nowrap; /* Prevents text from wrapping */ +} + +.brand { + font-size: clamp(1.1rem, 2vw, 1.5rem); + font-weight: 600; + letter-spacing: 0.2cm; + font-variant: small-caps; +} + +.subtitle { + font-size: 1rem; + letter-spacing: 0.05cm; +} + +a.site-nav-link, +.site-nav a { + color: var(--fg); + padding: 8px 10px; + border-radius: var(--r-2); +} + +a { + text-decoration: none; + font-weight: 600; + color: var(--primary); + transition: color var(--transition-1), text-shadow var(--transition-1); + scroll-margin-top: 80px; +} + +.site-nav a:hover { color: var(--accent); background-color: var(--surface-raised); } +.site-nav a:focus-visible { background-color: var(--surface-raised); box-shadow: var(--focus-ring); } + +.button { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 10px 14px; + max-width: 300px; + cursor: pointer; + font-size: 1rem; + border: 1px solid transparent; + border-radius: var(--r-2); + margin: 10px; + background: var(--primary); + color: var(--primary-contrast); + box-shadow: var(--shadow-1); + transition: transform var(--transition-1), box-shadow var(--transition-1), background var(--transition-1), color var(--transition-1); +} + +.button:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); } +.button:active { transform: translateY(0); box-shadow: var(--shadow-1); } +.button:focus-visible { outline: none; box-shadow: var(--focus-ring); } + +.button-ghost { background: transparent; color: var(--fg); border-color: var(--border); } +.button-ghost:hover { background: var(--surface-raised); } + +/* Variantes de boutons */ +.button.button-secondary { + background: transparent; + color: var(--primary); + border-color: var(--primary); +} +.button.button-secondary:hover { + background: var(--surface-raised); +} +.button.button-quiet { + background: transparent; + color: var(--muted); + border-color: transparent; +} +.button.button-quiet:hover { color: var(--fg); background: var(--surface-raised); } + +.button-grp { + display: flex; + flex-direction: row; + justify-content: center; + width: 100%; +} + +main { + flex: 1; +} + +section { + display: flex; + flex-direction: column; + padding: var(--space-6) var(--gutter); + width: min(100%, 60%); + margin: 20px auto; +} + +.courseNav { + display: block; + float: left; + position: fixed; + left: 0; + top: 150px; + max-width: 15%; + border: 1px solid; +} + +.edito { + border: 1px solid var(--border); + border-radius: var(--r-2); + background: var(--card); +} + +.comment { + font-weight: 500; +} + +.test { + border: 1px solid var(--border); + padding: 10px; +} + +.ul-arrow li { + list-style: none; +} + +.ul-arrow li:before { + content: "→ "; + font-size: 1.5rem; +} + +h1 { + color: var(--primary); + font-size: clamp(1.8rem, 4vw, 2.5rem); + border-left: 5px solid var(--accent); + border-bottom: 1px solid var(--accent); + padding-left: 10px; +} + +h2 { + color: var(--accent); + font-size: clamp(1.3rem, 3vw, 1.8rem); +} + +img { + max-width: 100%; +} + +.card-header img.thumbnails { + transition: transform 1s ease +} + +.card:hover img.thumbnails { + transform: scale(1.2); +} + +.container-inline { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 20px; +} + +.card { + display: flex; + flex-direction: column; + max-width: 250px; + border-left: 1px solid var(--border); + border-top: 1px solid var(--border); + border-radius: var(--r-2); + overflow: hidden; + margin: 20px; + background: var(--card); + box-shadow: var(--shadow-1); + transition: transform var(--transition-1), box-shadow var(--transition-1); +} + +.card:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); } + +.card-header h2 { + display: flex; + overflow: hidden; +} + +.card-header { + display: flex; + width: 100%; + font-size: 1rem; + font-weight: 500; +} + +.card-body a { + font-size: 1rem; + font-weight: 600; +} + +.card-body a:hover { + text-decoration: underline +} + +.card-body { + border-top: 1px solid var(--border); + padding: 5px; + line-height: 90%; + overflow: hidden; + z-index: 3; +} + +.def-author { + display: flex; + padding: 10px; + flex-direction: row; + align-items: center; + font-size: 0.8rem; + font-style: italic; +} + +/* CODE */ +.inline { + display: inline-block; +} + +pre { + border-radius: 5px; +} + +/* ====== Homepage enhancements ====== */ +.hero { + display: block; + width: 100%; + padding: 80px 20px 56px; + text-align: center; + background: + radial-gradient(1200px 600px at 50% -10%, rgba(78,158,214,0.20), transparent 60%), + radial-gradient(900px 400px at 80% 0%, rgba(255,122,28,0.12), transparent 60%), + var(--surface-raised); + position: relative; +} + +.hero-inner { + max-width: 960px; + margin: 0 auto; +} + +.hero-decor { + position: absolute; + inset: 0; + pointer-events: none; + background: + radial-gradient(80px 80px at 20% 30%, rgba(47,139,214,0.35), transparent 60%), + radial-gradient(120px 120px at 80% 20%, rgba(255,122,28,0.25), transparent 60%); + filter: blur(30px); + opacity: .6; +} + +.hero .hero-sub { + font-size: 1.125rem; + font-weight: 500; + margin: 10px auto 0; + max-width: 800px; +} + +.badge-row { display: flex; gap: 10px; justify-content: center; margin: 18px 0 6px; flex-wrap: wrap; } +.badge { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + border: 1px solid var(--border-subtle); + border-radius: 999px; + background: color-mix(in oklab, var(--card) 80%, transparent); + font-size: .9rem; + color: var(--text-muted); +} + +.hero-cta .button { + padding: 12px 18px; + font-size: 1rem; + border-radius: 6px; +} + +.cta-primary { + /* Make primary CTA more visible */ + font-weight: 700; + background: linear-gradient(180deg, + color-mix(in oklab, var(--primary) 92%, #ffffff 8%), + color-mix(in oklab, var(--primary) 82%, #000000 18%) + ); + border-color: color-mix(in oklab, var(--primary) 70%, #000000 30%); + box-shadow: + 0 8px 22px color-mix(in oklab, var(--primary) 35%, transparent), + 0 0 0 1px color-mix(in oklab, var(--primary) 40%, transparent); +} + +.cta-primary:hover { + transform: translateY(-2px); + background: linear-gradient(180deg, + color-mix(in oklab, var(--primary) 96%, #ffffff 4%), + color-mix(in oklab, var(--primary) 88%, #000000 12%) + ); + box-shadow: + 0 12px 28px color-mix(in oklab, var(--primary) 45%, transparent), + 0 0 0 1px color-mix(in oklab, var(--primary) 50%, transparent); +} + +.cta-primary:focus-visible { + outline: none; + box-shadow: + var(--focus-ring), + 0 8px 22px color-mix(in oklab, var(--primary) 40%, transparent), + 0 0 0 1px color-mix(in oklab, var(--primary) 55%, transparent); +} + +.cta-secondary { + /* Stronger outline for visibility next to the primary CTA */ + background: color-mix(in oklab, var(--card) 85%, transparent); + color: var(--primary); + border-color: var(--primary); + border-width: 2px; +} + +.cta-secondary:hover { + background: color-mix(in oklab, var(--card) 100%, transparent); + box-shadow: 0 6px 18px color-mix(in oklab, var(--primary) 25%, transparent); +} + +.hero-trust { + margin-top: 16px; + display: flex; + gap: 16px; + justify-content: center; + flex-wrap: wrap; + font-size: 0.95rem; + opacity: 0.9; +} + +.features { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 20px; + width: 90%; + max-width: 1100px; + margin: 10px auto 30px; +} + +.feature { + border: 1px solid var(--border); + border-radius: var(--r-3); + padding: 18px; + text-align: center; + box-shadow: var(--shadow-1); + transition: transform var(--transition-1), box-shadow var(--transition-1), border-color var(--transition-1); +} + +.feature .feat-ico { + font-size: 1.8rem; + margin-bottom: 8px; +} + +.feature:hover { + transform: translateY(-3px); + box-shadow: var(--shadow-2); + border-color: var(--border-strong); +} + +.pricing-teaser { + width: 60%; + margin: 10px auto 40px; + border: 1px solid var(--border); + border-radius: var(--r-3); + padding: 20px; + background: var(--card); +} + +/* Carousel (scroll-snap, lightweight) */ +.carousel { + width: min(100%, var(--container-w)); + margin: 0 auto var(--space-6); +} +.carousel-track { + display: grid; + grid-auto-flow: column; + grid-auto-columns: 80%; + gap: var(--space-4); + overflow-x: auto; + scroll-snap-type: x mandatory; + -webkit-overflow-scrolling: touch; + padding-bottom: var(--space-3); +} +.carousel-item { + scroll-snap-align: start; +} +.ratio-16x9 { position: relative; width: 100%; padding-top: 56.25%; overflow: hidden; border-radius: var(--r-2); } +.ratio-16x9 > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; } + +/* Hide scrollbar in supported browsers */ +.carousel-track { scrollbar-width: none; } +.carousel-track::-webkit-scrollbar { display: none; } + +/* Testimonials */ +.testimonials { + width: min(100%, var(--container-w)); + margin: 0 auto var(--space-6); + display: grid; + gap: var(--space-4); +} +.testimonials-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); } +.testimonial { + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--r-3); + padding: var(--space-5); + box-shadow: var(--shadow-1); + position: relative; +} +.testimonial .who { font-weight: 600; color: var(--fg); margin-top: var(--space-3); } +.testimonial p { position: relative; padding-left: 26px; } +.testimonial p:before { + content: "\201C"; + position: absolute; + left: 0; top: -6px; + font-size: 2rem; + color: var(--muted); + opacity: .5; + line-height: 1; +} + +/* Focus styles */ +:focus-visible { outline: none; box-shadow: var(--focus-ring); } + +/* Reduced motion */ +@media (prefers-reduced-motion: reduce) { + * { transition: none !important; animation: none !important; } +} + +/* Responsive */ +@media (max-width: 1024px) { + section { width: 80%; } + .features { grid-template-columns: repeat(2, 1fr); width: 90%; } + .testimonials-grid { grid-template-columns: repeat(2, 1fr); } + .carousel-track { grid-auto-columns: 88%; } +} + +@media (max-width: 640px) { + .site-nav { height: auto; padding: var(--space-2) var(--gutter); } + .navbar { width: 100%; } + section { width: 95%; margin: 16px auto; } + .features { grid-template-columns: 1fr; } + .hero { padding: 56px 16px 40px; } + .hero-cta .button { margin: 10px; } + .testimonials-grid { grid-template-columns: 1fr; } + .carousel-track { grid-auto-columns: 92%; } +} + +/* Steps for larger viewports to afficher plus d'éléments dans le carrousel */ +@media (min-width: 900px) { + .carousel-track { grid-auto-columns: 48%; } +} +@media (min-width: 1200px) { + .carousel-track { grid-auto-columns: 32%; } +} + +code { + font-family: "Courier New", Courier, monospace; + border-radius: 5px; +} + +.alert { + border: 1px solid var(--border); + border-radius: var(--r-2); + padding: 20px; + margin: 20px 0; +} + +.question { + background: url('../img/question.png') right no-repeat; +} + +#hide { + display: none; +} + +/* Footer v2 — site-footer */ +footer.site-footer { + display: block; + background-color: var(--card); + color: var(--text-muted); + border-top: 1px solid var(--border); + padding: var(--space-6) var(--gutter); + box-shadow: 0 -1px 0 rgba(0,0,0,0.05) inset; +} + +.footer-columns { + display: grid; + grid-template-columns: 2fr 1fr 1fr; + gap: var(--space-6); + max-width: var(--container-w); + margin: 0 auto var(--space-5); +} + +.footer-columns h5 { + margin: 0 0 var(--space-3) 0; + color: var(--fg); + font-size: 1rem; +} + +.footer-columns p, +.footer-columns li, +.footer-columns a { + font-size: 0.95rem; + line-height: 1.6; +} + +.footer-columns a { color: var(--link); text-decoration: none; } +.footer-columns a:hover { color: var(--link-hover); } + +.footer-link ul { + list-style: none; + margin: 0; + padding: 0; + display: grid; + gap: 8px; +} + +.social { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-wrap: wrap; + gap: 10px 16px; +} + +.social a { display: inline-flex; align-items: center; gap: 8px; } + +.footer-legal { + max-width: var(--container-w); + margin: 0 auto; + display: flex; + gap: 12px; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + color: var(--text-muted); + border-top: 1px dashed var(--border-subtle); + padding-top: var(--space-3); +} + +/* Legacy footer classes support (can be removed when not used elsewhere) */ +.footer { display: flex; flex-direction: row; justify-content: space-between; } +.about { width: 30%; } +.footer-link { width: 30%; } + +@media (max-width: 1000px) { + .courseNav { + display: block; + position: relative; + top: 0; + border: 0; + max-width: 100%; + box-shadow: 0 0 transparent; + background-color: transparent; + } +} + +@media (max-width: 678px) { + nav { + position: absolute; + height: auto; + align-items: flex-start; + justify-content: flex-start; + flex-direction: column; + width: 100%; + } + + .navbar { + width: 100%; + } + + .navbar ul { + flex-direction: column; + width: 100%; + } + + .navbar ul li { + width: 100%; + } + + .navbar ul ul { + position: static; + flex-direction: column; + } + + .navbar ul ul li { + width: 100%; + margin: 0; + } + + .navbar ul ul a { + padding: 10px 20px; + } + + /* Initialement, les sous-menus sont cachés */ + .navbar ul ul { + display: none; + } + + /* Afficher les sous-menus lorsque l'élément parent est focalisé */ + .navbar li:focus-within > ul { + display: flex; + } + + /* Styles pour les liens parents pour permettre le focus */ + .navbar a { + display: block; + padding: 10px; + text-decoration: none; + } + + .navbar li > a:focus + ul { + display: flex; + } + + section { margin: 32px auto; width: 90%; } + + .footer-columns { + grid-template-columns: 1fr; + } + + .footer-legal { + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + + .submenu { + display: none; + } +} + + +/* design.css */ +.snowflake { + position: fixed; + top: -10px; + color: var(--fg); + font-size: 1em; + opacity: 0.8; + filter: blur(1px); + z-index: 1000; /* Assurez-vous que les flocons sont au premier plan */ + pointer-events: none; /* Empêche les flocons d'interférer avec les clics */ + animation: fall linear infinite; +} + +@keyframes fall { + to { + transform: translateY(100vh); + } +} + +/* design.css */ +.light { + animation: blink 1s infinite; +} + +.light:nth-child(2) { + animation-delay: 0.2s; +} + +.light:nth-child(3) { + animation-delay: 0.4s; +} + +.light:nth-child(4) { + animation-delay: 0.6s; +} + +.light:nth-child(5) { + animation-delay: 0.8s; +} + +.light:nth-child(6) { + animation-delay: 1s; +} + +.light:nth-child(7) { + animation-delay: 1.2s; +} + +.light:nth-child(8) { + animation-delay: 1.4s; +} + +.light:nth-child(9) { + animation-delay: 1.6s; +} + +.light:nth-child(10) { + animation-delay: 1.8s; +} + +@keyframes blink { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +/* Dark theme stylesheet — relies on design tokens */ + +html { background: var(--bg); color: var(--fg); } +nav.site-nav { background: var(--nav-bg); } + +.navbar ul ul { background: var(--elev); border-color: var(--border); box-shadow: var(--shadow-1); } +.navbar ul ul a { color: var(--muted); } + +.brand { color: var(--fg); } +a { color: var(--primary); } +a:hover { color: var(--accent); text-shadow: none; } + +.courseNav { border-color: var(--border); box-shadow: var(--shadow-1); background-color: var(--card); } +.edito { border-color: var(--border); background-color: var(--card); color: var(--muted); } +.test { border-color: var(--border); background-color: var(--surface); color: var(--fg); } + +h1 { color: var(--primary); border-left-color: var(--accent); border-bottom-color: var(--accent); } +h2 { color: var(--accent); } + +.card { border-color: var(--border); box-shadow: var(--shadow-1); background: var(--card); } +.card-body a { color: var(--primary); } +.card-body a:hover { color: var(--link-hover); } + +th { border-color: var(--border); } +tr:nth-child(even) { background-color: var(--neutral-300); color: var(--fg); } + +footer { background-color: var(--neutral-0); color: var(--text-muted); } + +/* Homepage sections */ +.hero { background: var(--surface-raised); } +.features .feature { border-color: var(--border); background-color: var(--card); box-shadow: var(--shadow-1); } +.pricing-teaser { border-color: var(--border); background-color: var(--card); } + +/* --- MISE EN PAGE HERO (SPLIT SCREEN) --- */ + +/* Par défaut (mobile), les éléments s'empilent avec un espace */ +.hero-split { + display: flex; + flex-direction: column; + gap: 40px; +} + +/* Sur grand écran (Desktop), on passe en 2 colonnes */ +@media (min-width: 992px) { + .hero-split { + /* Reste en colonne aussi sur desktop comme demandé */ + flex-direction: column; + align-items: stretch; + justify-content: flex-start; + gap: 60px; /* Espace entre le texte et le visuel */ + } + + .hero-text { + /* En colonne, ne pas forcer l'expansion verticale */ + flex: 0 0 auto; + max-width: 100%; + } + + .hero-visual { + flex: 0 0 auto; + width: 100%; /* S'assure que le visuel prend la largeur */ + max-width: 100%; + perspective: 1000px; /* Pour un petit effet 3D si souhaité plus tard */ + } +} + +/* --- STYLE DE LA FENÊTRE DE CODE (TECH VISUAL) --- */ + +.code-window { + /* Fond sombre style éditeur moderne (ex: Dracula/Catppuccin) */ + background: #1e1e2e; + border-radius: 12px; + /* Une ombre portée pour donner de la profondeur */ + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); + overflow: hidden; /* Pour que les coins arrondis fonctionnent */ + border: 1px solid rgba(255, 255, 255, 0.05); + font-family: 'Fira Code', 'Roboto Mono', 'Courier New', monospace; /* Important: police monospace */ + transform: translateY(0); + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +/* Petit effet interactif au survol */ +.code-window:hover { + transform: translateY(-5px); + box-shadow: 0 30px 60px -12px rgba(0, 0, 0, 0.6); +} + +/* HEADER DE LA FENÊTRE (Style macOS) */ +.window-header { + background: #28293d; /* Légèrement plus clair que le fond */ + padding: 12px 16px; + display: flex; + align-items: center; + position: relative; + border-bottom: 1px solid rgba(0,0,0,0.2); +} + +/* Les 3 petits points colorés */ +.dots { + display: flex; + gap: 8px; + z-index: 2; +} +.dot { width: 12px; height: 12px; border-radius: 50%; } +.dot.red { background: #ff5f56; } +.dot.yellow { background: #ffbd2e; } +.dot.green { background: #27c93f; } + +/* Le nom du fichier centré */ +.filename { + position: absolute; + left: 0; + right: 0; + text-align: center; + color: #a9a9b3; + font-size: 0.85rem; + font-weight: 500; + user-select: none; /* Empêche de sélectionner le texte du titre */ +} + +/* CONTENU DU CODE */ +.window-content { + padding: 24px; + text-align: left; /* Force l'alignement à gauche même si le parent est centré */ +} + +.code-block { + margin: 0; + color: #cdd6f4; /* Couleur de base du texte */ + line-height: 1.7; + font-size: 15px; + white-space: pre; /* Respecte les espaces et sauts de ligne */ + overflow-x: auto; /* Scroll si le code est trop long */ +} + +/* --- COLORATION SYNTAXIQUE MANUELLE (Style Dracula/Catppuccin) --- */ + +/* Numéros de ligne */ +.line { + display: block; + counter-increment: line-counter; +} +.code-block { counter-reset: line-counter; } +.line::before { + content: counter(line-counter); + display: inline-block; + width: 30px; + margin-right: 20px; + color: #5c5f77; /* Gris foncé pour les numéros */ + text-align: right; + user-select: none; +} + +/* Couleurs des mots-clés (Basé sur les classes span ajoutées dans le HTML) */ +.qn { color: #cba6f7; font-weight: bold; } /* def, if, return (Mauve) */ +.fn { color: #89b4fa; } /* Noms de fonctions (Bleu) */ +.st { color: #a6e3a1; } /* Strings / Textes (Vert) */ +.kw { color: #f9e2af; } /* Booléens, valeurs clés (Jaune/Orange) */ +.cm { color: #6c7086; font-style: italic; } /* Commentaires (Gris) */ +.ow { color: #cba6f7; } /* Opérateurs and/or (Mauve) */ + +/* Light theme stylesheet — relies on design tokens */ + +html { background: var(--bg); color: var(--fg); } +nav.site-nav { background: var(--nav-bg); } + +/* Links & buttons inherit from design.css using tokens. Only minimal tweaks here. */ +.navbar ul ul { background: var(--elev); border-color: var(--border); box-shadow: var(--shadow-1); } +.navbar ul ul a { color: var(--muted); } + +.brand { color: var(--fg); } +a { color: var(--primary); } +a:hover { color: var(--accent); text-shadow: none; } + +.courseNav { border-color: var(--border); box-shadow: var(--shadow-1); background-color: var(--card); } +.edito { border-color: var(--border); background-color: var(--card); } +.test { border-color: var(--border); background-color: var(--neutral-200); color: var(--fg); } + +h1 { color: var(--primary); border-left-color: var(--accent); border-bottom-color: var(--accent); } +h2 { color: var(--accent); } + +.card { border-color: var(--border); box-shadow: var(--shadow-1); background: var(--card); } +.card-body a { color: var(--primary); } +.card-body a:hover { color: var(--link-hover); } + +th { border-color: var(--border); } +tr:nth-child(even) { background-color: var(--neutral-200); } + +footer { background-color: var(--card); color: var(--text-muted); } + +/* FORUMULAIRES */ + +form { + display: flex; + flex-direction: column; + width: 50%; + margin: 20px auto; +} + +/* Styles pour la section du formulaire */ +.form-section { + display: flex; + flex-direction: column; + align-items: center; + padding: 20px; + background-color: var(--elev); /* Fond surélevé via token */ + border: 1px solid var(--border); /* Bordure légère */ + border-radius: 10px; /* Bordure arrondie */ + box-shadow: var(--shadow-1); /* Ombre légère */ + margin: 20px auto; + width: 50%; +} + +.login-form { + display: flex; + flex-direction: column; + width: 100%; + align-items: center; +} + +.form-group { + display: flex; + flex-direction: column; + margin-bottom: 15px; +} + +.form-group label { + margin-bottom: 5px; + font-weight: bold; +} + +.login-form input[type="text"], +.login-form input[type="email"], +.login-form input[type="password"], +.login-form textarea { + padding: 10px; + border: 1px solid var(--border); /* Gris clair */ + border-radius: 5px; /* Bordure légèrement arrondie */ + background-color: var(--surface); /* Fond de surface */ + color: var(--text-muted); + margin-bottom: 10px; + font-size: 1rem; /* Taille de police harmonisée */ + box-shadow: inset var(--shadow-1); /* Ombre intérieure subtile */ +} + +.login-form .btn-submit { + /* Aligne le bouton du formulaire sur le système de thème */ + display: inline-flex; + align-items: center; + gap: 8px; + padding: 10px 16px; + font: inherit; + font-weight: 600; + line-height: 1.2; + border-radius: var(--r-2); + border: 1px solid var(--primary); + background-color: var(--primary); + color: var(--primary-contrast); + box-shadow: var(--shadow-1); + cursor: pointer; + transition: background-color var(--transition-1), border-color var(--transition-1), transform var(--transition-1), box-shadow var(--transition-1); +} + +.login-form .btn-submit:hover { + background-color: var(--link-hover); +} + +input[type="text"], input[type="email"], input[type="password"], textarea { + padding: 10px; + border: 1px solid var(--border); /* Gris foncé */ + border-radius: 2px; + background-color: var(--surface); /* Gris clair harmonisé */ + color: var(--fg); +} + +/* + Système de boutons — harmonisé avec le thème + Utilise les tokens de couleurs et de rayons définis en haut de fichier. +*/ + +/* Base */ +.btn, .button, input[type="submit"], button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 10px 16px; + font: inherit; + font-weight: 600; + line-height: 1.2; + border-radius: var(--r-2); + border: 1px solid var(--border); + background-color: var(--surface); + color: var(--text); + box-shadow: var(--shadow-1); + cursor: pointer; + text-decoration: none; + transition: background-color var(--transition-1), border-color var(--transition-1), transform var(--transition-1), box-shadow var(--transition-1), color var(--transition-1); +} + +.btn:hover, .button:hover, input[type="submit"]:hover, button:hover { + background-color: var(--neutral-300); +} + +.btn:active, .button:active, input[type="submit"]:active, button:active { + transform: translateY(1px); + box-shadow: var(--shadow-1); +} + +.btn:focus-visible, .button:focus-visible, input[type="submit"]:focus-visible, button:focus-visible { + outline: none; + box-shadow: var(--focus-ring); +} + +.btn[disabled], .button[disabled], input[type="submit"][disabled], button[disabled] { + opacity: 0.6; + cursor: not-allowed; +} + +/* Variantes */ +.btn-primary, .button-primary { + background-color: var(--primary); + border-color: var(--primary); + color: var(--primary-contrast); +} + +.btn-primary:hover, .button-primary:hover { + background-color: var(--link-hover); + border-color: var(--link-hover); +} + +.btn-secondary, .button-secondary { + background-color: var(--neutral-500); + border-color: var(--neutral-500); + color: var(--success-contrast); +} + +.btn-secondary:hover, .button-secondary:hover { + background-color: var(--neutral-600); + border-color: var(--neutral-600); +} + +/* Light theme: boutons plus colorés (secondaire = accent) */ +[data-theme='light'] .button.button-secondary, +[data-theme='light'] .btn-secondary, +[data-theme='light'] .button-secondary { + background-color: var(--accent); + border-color: var(--accent); + color: #ffffff; +} +[data-theme='light'] .button.button-secondary:hover, +[data-theme='light'] .btn-secondary:hover, +[data-theme='light'] .button-secondary:hover { + filter: brightness(0.95); +} + +.btn-outline, .button-outline { + background: transparent; + color: var(--text); + border-color: var(--border); +} + +.btn-outline:hover, .button-outline:hover { + background: var(--neutral-300); +} + +.btn-ghost, .button-ghost { + background: transparent; + border-color: transparent; + color: var(--text); + box-shadow: none; +} + +.btn-ghost:hover, .button-ghost:hover { + background: var(--neutral-300); +} + +.btn-danger, .button-danger { + background-color: var(--danger); + border-color: var(--danger); + color: var(--danger-contrast); +} + +.btn-danger:hover, .button-danger:hover { + filter: brightness(1.05); +} + +/* Tailles */ +.btn-sm, .button-sm { padding: 6px 10px; font-size: 0.9rem; } +.btn-lg, .button-lg { padding: 12px 20px; font-size: 1.05rem; } + +/* Compat: mappe les anciens sélecteurs spécifiques vers le système */ +.login-form .btn-submit { /* déjà défini ci-dessus comme primaire */ } + +/* PROFILE */ + +.profile-section { + max-width: 800px; + margin: 0 auto; + padding: 20px; + border-radius: 8px; + box-shadow: var(--shadow-1); +} + +.profile-header { + text-align: center; + margin-bottom: 20px; +} + +.profile-header h2 { + font-size: 2em; + margin-bottom: 10px; +} + +.profile-picture { + width: 150px; + height: 150px; + border-radius: 50%; + object-fit: cover; + margin-bottom: 20px; +} + +.profile-picture-mini { + width: 30px; + height: 30px; + border-radius: 50%; + object-fit: cover; + margin: 10px; +} + +.profile-details p { + font-size: 1.1em; + margin: 10px 0; +} + +.profile-actions { + text-align: center; + margin-top: 20px; +} + +.profile-actions .btn { + margin: 0 10px; + padding: 10px 20px; + font-size: 1em; + border-radius: 5px; + text-decoration: none; +} + +.profile-actions .btn-primary { + background-color: var(--primary); + color: var(--primary-contrast); +} + +.profile-actions .btn-secondary { + background-color: var(--neutral-700); + color: var(--success-contrast); +} + +.profile-nav { + display: flex; + border-bottom: 1px solid var(--border); /* Bordure légère */ + margin: 20px auto; /* Centrer la navigation */ +} + +.profile-nav ul { + list-style: none; /* Supprimer les puces */ + display: flex; + flex-direction: row; + padding: 0; + margin: 0; +} + +.profile-nav li { + margin-bottom: 10px; /* Espacement entre les éléments */ +} + +.profile-nav a { + text-decoration: none; /* Supprimer la décoration de texte */ + color: var(--link); /* Couleur du texte */ + font-weight: bold; /* Texte en gras */ + display: block; /* Afficher les liens comme des blocs */ + padding: 10px; /* Espacement interne */ + border-radius: 5px; /* Bordure arrondie */ + transition: background-color 0.3s ease; /* Transition pour le survol */ +} + +.profile-nav a:hover { + background-color: var(--neutral-300); /* Couleur de fond au survol */ +} + +/* Redesigned social-style profile */ +.profile-cover { + position: relative; + border-radius: 12px; + overflow: hidden; + box-shadow: var(--shadow-1); + margin-bottom: 20px; +} + +.profile-cover-bg { + height: 140px; + background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); + opacity: 0.25; +} + +.profile-cover-content { + display: grid; + grid-template-columns: auto 1fr auto; + gap: 16px; + align-items: center; + padding: 12px 16px 16px 16px; + background: var(--surface); +} + +.profile-avatar { + width: 96px; + height: 96px; + border-radius: 50%; + object-fit: cover; + border: 3px solid var(--surface); + margin-top: -68px; + box-shadow: var(--shadow-2); +} + +.profile-identity { + display: flex; + flex-direction: column; + gap: 6px; +} + +.profile-name { margin: 0; font-size: 1.6rem; } +.profile-username { color: var(--text-muted); font-size: 0.95rem; } +.profile-bio { margin: 4px 0 0 0; color: var(--fg); } + +.profile-meta { + display: flex; + gap: 16px; + margin: 8px 0 0 0; + padding: 0; + list-style: none; + color: var(--text-muted); + font-size: 0.95rem; +} + +.profile-meta i { color: var(--text-muted); margin-right: 6px; } + +.profile-actions-rail { display: flex; gap: 8px; } + +.profile-tabs { + border-bottom: 1px solid var(--border); + margin-bottom: 16px; +} + +.profile-tabs ul { + display: flex; + gap: 12px; + list-style: none; + margin: 0; + padding: 0 4px; +} + +.profile-tabs li a { + display: inline-block; + padding: 10px 12px; + border-radius: 8px 8px 0 0; + text-decoration: none; + color: var(--link); +} + +.profile-tabs li.active a { + background: var(--surface); + color: var(--fg); + border: 1px solid var(--border); + border-bottom-color: transparent; +} + +.profile-content { margin-top: 12px; } + +.profile-grid { + display: grid; + grid-template-columns: 2fr 1fr; + gap: 16px; +} + +@media (max-width: 900px) { + .profile-cover-content { grid-template-columns: auto 1fr; } + .profile-actions-rail { grid-column: 1 / -1; } + .profile-grid { grid-template-columns: 1fr; } +} + +.profile-card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 10px; + padding: 16px; + box-shadow: var(--shadow-1); +} + +.profile-about-list { + list-style: none; + padding: 0; + margin: 0 0 8px 0; +} + +.profile-about-list li { margin: 6px 0; } + +.profile-bio-body { margin-top: 8px; line-height: 1.5; } + +.profile-courses { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 10px; + list-style: none; + padding: 0; + margin: 0 0 10px 0; +} + +.profile-courses li a { + display: flex; + align-items: center; + gap: 10px; + text-decoration: none; + color: var(--fg); + padding: 8px; + border: 1px solid var(--border); + border-radius: 8px; +} + +.course-thumb-mini { + width: 40px; + height: 40px; + object-fit: cover; + border-radius: 6px; +} + +.muted { color: var(--text-muted); } +.text-right { text-align: right; } + +/* TABLEAUX */ + +table { + width: 80%; + border-collapse: collapse; +} + +.table-40 { + width: 40%; +} + +.table-50 { + width: 50%; +} + +.table-60 { + width: 60%; +} + +.table-70 { + width: 70%; +} + +th { + border-bottom: 2px solid; + text-align: left; + font-size: 1.1rem; + padding: 5px; +} + +td { + padding: 10px; +} + +/* UTILS */ + +span.helptext { + font-size: 0.7em; + color: var(--text-muted); + margin-left: 5px; + font-style: italic; +} + +ul.flash_messages { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin: 10px; + position: fixed; + top: 70px; + right: 10px; + z-index: 1000; +} + +ul.flash_message { + background-color: var(--surface); + border: 1px solid var(--border); + padding: 10px; + margin-bottom: 10px; + border-radius: 5px; + opacity: 1; + transition: opacity 1s ease-out; /* Transition pour l'opacité */ +} + +ul.flash_messages li { + list-style: none; + padding: 10px; + margin: 10px; + border-radius: 5px; + color: var(--fg); +} + +ul.flash_messages li.error { + background-color: var(--danger); + color: var(--danger-contrast); +} + +ul.flash_messages li.success { + background-color: var(--success); + color: var(--success-contrast); +} \ No newline at end of file diff --git a/static/css/colors_dark.css b/static/css/colors_dark.css new file mode 100644 index 0000000..fa0ae41 --- /dev/null +++ b/static/css/colors_dark.css @@ -0,0 +1,11 @@ +/* Theme bridge (DARK) — 2025 design system + Minimal overrides; colors and components come from app.css tokens. */ +html { + background-color: var(--bg); + color: var(--text); + color-scheme: dark; +} + +nav.site-nav { + background: var(--nav-bg); +} diff --git a/static/css/colors_light.css b/static/css/colors_light.css new file mode 100644 index 0000000..4e5e059 --- /dev/null +++ b/static/css/colors_light.css @@ -0,0 +1,14 @@ +/* Theme bridge (LIGHT) — 2025 design system + This file intentionally contains minimal overrides. The full palette, + components and tokens live in css/app.css using CSS variables. + Keeping this file ensures JS can swap theme stylesheets without FOUC. */ + +/* Prefer light-friendly scrollbar & subtle touches without overriding tokens */ +html[data-theme='light'] { + color-scheme: light; +} + +/* Optional: ensure nav backdrop looks crisp on light */ +nav.site-nav { + background: var(--nav-bg); +} diff --git a/templates/home.html b/templates/home.html index 925ab23..77ae7c2 100644 --- a/templates/home.html +++ b/templates/home.html @@ -2,18 +2,54 @@ {% block content %}
-
-

Apprenez à coder de A à Z

-

Des cours gratuits et payants, structurés et concrets, pour progresser rapidement en programmation.

-
- Commencer gratuitement - Voir les cours + +
+ +
+

Apprenez à coder de A à Z

+

Des cours gratuits et payants, structurés et concrets, pour progresser rapidement en programmation.

+ + +
+ Pas de carte requise pour commencer + À votre rythme + Accès premium en option +
-
- Pas de carte requise pour commencer - À votre rythme - Accès premium en option + +
+
+
+
+ + + +
+ main.py — PartirDeZero +
+
+
def devenir_dev_autonome(etudiant):
+    # Objectif: Maîtriser Python & Django
+    motivation = True
+    projet_concret = "Mon Portfolio"
+
+    if motivation and etudiant.suit_le_cours():
+        etudiant.skills.append("Backend")
+        return f"Succès : {projet_concret} déployé !"
+
+# Lancez votre carrière aujourd'hui
+print(devenir_dev_autonome("Toi"))
+
+
+
@@ -37,7 +73,7 @@ {% block course %} {% include "courses/partials/list.html" %} {% endblock %} - +

Gratuit pour commencer, Premium pour aller plus loin

-