We use cookies to enhance your experience on the site
CodeWorlds

Project - Modern Portfolio Page with Advanced CSS Techniques

It's time to create a comprehensive project that will showcase all the advanced CSS techniques you've learned in this module. We'll create a modern portfolio page utilizing the latest CSS capabilities.

Project Description

You'll create a responsive portfolio page for a web developer that will include:

  • A hero section with scroll-driven animations
  • Project portfolio with container queries
  • A skills section with CSS Grid and Subgrid
  • A contact form with advanced validation
  • Dark/light mode toggle
  • Advanced animations and micro-interactions

Functional Requirements

1. Page Structure

1<!DOCTYPE html>
2<html lang="en">
3<head>
4  <meta charset="UTF-8">
5  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6  <title>Jan Kowalski - Web Developer Portfolio</title>
7  <link rel="stylesheet" href="styles.css">
8</head>
9<body>
10  <header class="header">
11    <nav class="nav">
12      <div class="nav__brand">
13        <a href="#" class="nav__logo">JK</a>
14      </div>
15      <ul class="nav__menu">
16        <li class="nav__item">
17          <a href="#home" class="nav__link">Home</a>
18        </li>
19        <li class="nav__item">
20          <a href="#about" class="nav__link">About</a>
21        </li>
22        <li class="nav__item">
23          <a href="#projects" class="nav__link">Projects</a>
24        </li>
25        <li class="nav__item">
26          <a href="#skills" class="nav__link">Skills</a>
27        </li>
28        <li class="nav__item">
29          <a href="#contact" class="nav__link">Contact</a>
30        </li>
31      </ul>
32      <button class="theme-toggle" aria-label="Toggle theme">
33        <span class="theme-toggle__icon">🌙</span>
34      </button>
35    </nav>
36  </header>
37
38  <main>
39    <section id="home" class="hero">
40      <div class="hero__content">
41        <h1 class="hero__title">Jan Kowalski</h1>
42        <p class="hero__subtitle">Frontend Developer & UI/UX Designer</p>
43        <a href="#projects" class="hero__cta">View My Projects</a>
44      </div>
45      <div class="hero__animation">
46        <div class="floating-shapes">
47          <div class="shape shape--1"></div>
48          <div class="shape shape--2"></div>
49          <div class="shape shape--3"></div>
50        </div>
51      </div>
52    </section>
53
54    <section id="about" class="about">
55      <div class="container">
56        <h2 class="section__title">About Me</h2>
57        <div class="about__content">
58          <div class="about__text">
59            <p>I am passionate about creating modern web applications...</p>
60          </div>
61          <div class="about__image">
62            <img src="profile.jpg" alt="Profile photo" loading="lazy">
63          </div>
64        </div>
65      </div>
66    </section>
67
68    <section id="projects" class="projects">
69      <div class="container">
70        <h2 class="section__title">My Projects</h2>
71        <div class="projects__grid">
72          <article class="project-card" data-category="react">
73            <div class="project-card__image">
74              <img src="project1.jpg" alt="Project 1" loading="lazy">
75            </div>
76            <div class="project-card__content">
77              <h3 class="project-card__title">E-commerce Dashboard</h3>
78              <p class="project-card__description">A modern dashboard for managing an online store.</p>
79              <div class="project-card__tech">
80                <span class="tech-tag">React</span>
81                <span class="tech-tag">TypeScript</span>
82                <span class="tech-tag">Tailwind</span>
83              </div>
84              <div class="project-card__actions">
85                <a href="#" class="btn btn--primary">Live Demo</a>
86                <a href="#" class="btn btn--secondary">GitHub</a>
87              </div>
88            </div>
89          </article>
90
91          <!-- More project cards... -->
92        </div>
93      </div>
94    </section>
95
96    <section id="skills" class="skills">
97      <div class="container">
98        <h2 class="section__title">Skills</h2>
99        <div class="skills__grid">
100          <div class="skill-category">
101            <h3 class="skill-category__title">Frontend</h3>
102            <div class="skill-category__items">
103              <div class="skill-item">
104                <span class="skill-item__name">HTML/CSS</span>
105                <div class="skill-item__bar">
106                  <div class="skill-item__progress" data-level="95"></div>
107                </div>
108              </div>
109              <!-- More skills... -->
110            </div>
111          </div>
112        </div>
113      </div>
114    </section>
115
116    <section id="contact" class="contact">
117      <div class="container">
118        <h2 class="section__title">Contact</h2>
119        <form class="contact-form" novalidate>
120          <div class="form-group">
121            <label for="name" class="form-label">Name</label>
122            <input type="text" id="name" name="name" class="form-input" required>
123            <span class="form-error" id="name-error"></span>
124          </div>
125          <div class="form-group">
126            <label for="email" class="form-label">Email</label>
127            <input type="email" id="email" name="email" class="form-input" required>
128            <span class="form-error" id="email-error"></span>
129          </div>
130          <div class="form-group">
131            <label for="message" class="form-label">Message</label>
132            <textarea id="message" name="message" class="form-textarea" required></textarea>
133            <span class="form-error" id="message-error"></span>
134          </div>
135          <button type="submit" class="btn btn--primary btn--full">Send Message</button>
136        </form>
137      </div>
138    </section>
139  </main>
140
141  <footer class="footer">
142    <div class="container">
143      <p>&copy; 2024 Jan Kowalski. All rights reserved.</p>
144    </div>
145  </footer>
146
147  <script src="script.js"></script>
148</body>
149</html>

2. CSS Using Advanced Techniques

1/* ===== CSS VARIABLES & DESIGN TOKENS ===== */
2:root {
3  /* Colors - Light theme */
4  --color-primary: #667eea;
5  --color-secondary: #764ba2;
6  --color-accent: #f093fb;
7  --color-text: #2d3748;
8  --color-text-muted: #718096;
9  --color-bg: #ffffff;
10  --color-surface: #f7fafc;
11  --color-border: #e2e8f0;
12
13  /* Typography */
14  --font-primary: 'Inter', system-ui, sans-serif;
15  --font-heading: 'Poppins', sans-serif;
16
17  /* Spacing */
18  --space-xs: 0.25rem;
19  --space-sm: 0.5rem;
20  --space-md: 1rem;
21  --space-lg: 1.5rem;
22  --space-xl: 2rem;
23  --space-2xl: 3rem;
24  --space-3xl: 4rem;
25
26  /* Layout */
27  --container-max-width: 1200px;
28  --border-radius: 0.5rem;
29  --border-radius-lg: 1rem;
30
31  /* Shadows */
32  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
33  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
34  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
35
36  /* Transitions */
37  --transition-fast: 150ms ease;
38  --transition-normal: 250ms ease;
39  --transition-slow: 350ms ease;
40}
41
42/* Dark theme variables */
43[data-theme="dark"] {
44  --color-text: #f7fafc;
45  --color-text-muted: #a0aec0;
46  --color-bg: #1a202c;
47  --color-surface: #2d3748;
48  --color-border: #4a5568;
49}
50
51/* ===== CASCADE LAYERS ===== */
52@layer reset, base, components, utilities;
53
54@layer reset {
55  *, *::before, *::after {
56    box-sizing: border-box;
57    margin: 0;
58    padding: 0;
59  }
60
61  html {
62    scroll-behavior: smooth;
63  }
64
65  body {
66    line-height: 1.6;
67    -webkit-font-smoothing: antialiased;
68  }
69
70  img, picture, video, canvas, svg {
71    display: block;
72    max-width: 100%;
73  }
74
75  input, button, textarea, select {
76    font: inherit;
77  }
78}
79
80@layer base {
81  body {
82    font-family: var(--font-primary);
83    color: var(--color-text);
84    background: var(--color-bg);
85    transition: background-color var(--transition-normal),
86                color var(--transition-normal);
87  }
88
89  h1, h2, h3, h4, h5, h6 {
90    font-family: var(--font-heading);
91    font-weight: 600;
92    line-height: 1.2;
93  }
94
95  a {
96    color: var(--color-primary);
97    text-decoration: none;
98    transition: color var(--transition-fast);
99
100    &:hover {
101      color: var(--color-secondary);
102    }
103  }
104}
105
106/* ===== CONTAINER QUERIES ===== */
107.projects {
108  container-type: inline-size;
109  container-name: projects;
110}
111
112.project-card {
113  background: var(--color-surface);
114  border-radius: var(--border-radius-lg);
115  overflow: hidden;
116  box-shadow: var(--shadow-md);
117  transition: transform var(--transition-normal),
118              box-shadow var(--transition-normal);
119
120  &:hover {
121    transform: translateY(-4px);
122    box-shadow: var(--shadow-lg);
123  }
124}
125
126@container projects (min-width: 768px) {
127  .projects__grid {
128    display: grid;
129    grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
130    gap: var(--space-xl);
131  }
132
133  .project-card {
134    display: grid;
135    grid-template-rows: 200px 1fr;
136  }
137}
138
139@container projects (min-width: 1024px) {
140  .projects__grid {
141    grid-template-columns: repeat(3, 1fr);
142  }
143}
144
145/* ===== CSS GRID WITH SUBGRID ===== */
146.skills__grid {
147  display: grid;
148  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
149  gap: var(--space-xl);
150}
151
152.skill-category {
153  display: grid;
154  grid-template-rows: auto 1fr;
155  background: var(--color-surface);
156  border-radius: var(--border-radius-lg);
157  padding: var(--space-lg);
158
159  /* Use subgrid when supported */
160  @supports (grid-template-rows: subgrid) {
161    grid-template-rows: subgrid;
162  }
163}
164
165/* ===== CSS NESTING ===== */
166.nav {
167  display: flex;
168  justify-content: space-between;
169  align-items: center;
170  padding: var(--space-md) var(--space-lg);
171  background: var(--color-surface);
172  backdrop-filter: blur(10px);
173  border-bottom: 1px solid var(--color-border);
174
175  &__brand {
176    font-weight: bold;
177    font-size: 1.5rem;
178  }
179
180  &__logo {
181    display: inline-flex;
182    align-items: center;
183    justify-content: center;
184    width: 40px;
185    height: 40px;
186    background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
187    color: white;
188    border-radius: 50%;
189    font-weight: bold;
190  }
191
192  &__menu {
193    display: flex;
194    list-style: none;
195    gap: var(--space-lg);
196
197    @media (max-width: 768px) {
198      display: none; /* Mobile menu to be implemented */
199    }
200  }
201
202  &__link {
203    position: relative;
204    font-weight: 500;
205
206    &::after {
207      content: '';
208      position: absolute;
209      bottom: -4px;
210      left: 0;
211      width: 0;
212      height: 2px;
213      background: linear-gradient(90deg, var(--color-primary), var(--color-secondary));
214      transition: width var(--transition-normal);
215    }
216
217    &:hover::after,
218    &.active::after {
219      width: 100%;
220    }
221  }
222}
223
224/* ===== SCROLL-DRIVEN ANIMATIONS ===== */
225@supports (animation-timeline: scroll()) {
226  .hero__title {
227    animation: hero-fade-in linear;
228    animation-timeline: view();
229    animation-range: entry 0% cover 40%;
230  }
231
232  .section__title {
233    animation: slide-up linear;
234    animation-timeline: view();
235    animation-range: entry 20% cover 50%;
236  }
237
238  @keyframes hero-fade-in {
239    from {
240      opacity: 0;
241      transform: translateY(50px) scale(0.9);
242    }
243    to {
244      opacity: 1;
245      transform: translateY(0) scale(1);
246    }
247  }
248
249  @keyframes slide-up {
250    from {
251      opacity: 0;
252      transform: translateY(30px);
253    }
254    to {
255      opacity: 1;
256      transform: translateY(0);
257    }
258  }
259}
260
261/* ===== MODERN CSS FEATURES ===== */
262.hero {
263  min-height: 100vh;
264  display: grid;
265  grid-template-columns: 1fr 1fr;
266  align-items: center;
267  padding: var(--space-2xl);
268  background: linear-gradient(135deg,
269    color-mix(in srgb, var(--color-primary) 10%, transparent) 0%,
270    color-mix(in srgb, var(--color-secondary) 10%, transparent) 100%
271  );
272  position: relative;
273  overflow: hidden;
274
275  @media (max-width: 768px) {
276    grid-template-columns: 1fr;
277    text-align: center;
278    padding: var(--space-xl) var(--space-lg);
279  }
280}
281
282.floating-shapes {
283  position: absolute;
284  inset: 0;
285  pointer-events: none;
286}
287
288.shape {
289  position: absolute;
290  border-radius: 50%;
291  background: linear-gradient(45deg, var(--color-primary), var(--color-accent));
292  opacity: 0.1;
293  animation: float 6s ease-in-out infinite;
294
295  &--1 {
296    width: 100px;
297    height: 100px;
298    top: 20%;
299    left: 10%;
300    animation-delay: 0s;
301  }
302
303  &--2 {
304    width: 150px;
305    height: 150px;
306    top: 60%;
307    right: 20%;
308    animation-delay: 2s;
309  }
310
311  &--3 {
312    width: 80px;
313    height: 80px;
314    bottom: 20%;
315    left: 60%;
316    animation-delay: 4s;
317  }
318}
319
320@keyframes float {
321  0%, 100% {
322    transform: translateY(0) rotate(0deg);
323  }
324  50% {
325    transform: translateY(-20px) rotate(180deg);
326  }
327}
328
329/* ===== SKILL ITEMS ===== */
330.skill-item {
331  margin-bottom: var(--space-md);
332
333  &__name {
334    display: block;
335    margin-bottom: var(--space-xs);
336    font-weight: 500;
337  }
338
339  &__bar {
340    width: 100%;
341    height: 8px;
342    background: var(--color-border);
343    border-radius: 4px;
344    overflow: hidden;
345  }
346
347  &__progress {
348    height: 100%;
349    background: linear-gradient(90deg, var(--color-primary), var(--color-secondary));
350    border-radius: 4px;
351    transform-origin: left;
352    animation: progress-fill 2s ease-out;
353
354    @supports (animation-timeline: view()) {
355      animation: progress-fill linear;
356      animation-timeline: view();
357      animation-range: entry 50% cover 80%;
358    }
359  }
360}
361
362@keyframes progress-fill {
363  from {
364    transform: scaleX(0);
365  }
366  to {
367    transform: scaleX(1);
368  }
369}
370
371/* ===== THEME TOGGLE ===== */
372.theme-toggle {
373  background: var(--color-surface);
374  border: 2px solid var(--color-border);
375  border-radius: 50%;
376  width: 44px;
377  height: 44px;
378  display: flex;
379  align-items: center;
380  justify-content: center;
381  cursor: pointer;
382  transition: all var(--transition-normal);
383
384  &:hover {
385    transform: scale(1.1);
386    background: var(--color-primary);
387    border-color: var(--color-primary);
388    color: white;
389  }
390
391  &__icon {
392    font-size: 1.2rem;
393    transition: transform var(--transition-normal);
394  }
395
396  &[data-theme="dark"] &__icon {
397    transform: rotate(180deg);
398  }
399}
400
401/* ===== FORM STYLING ===== */
402.contact-form {
403  max-width: 600px;
404  margin: 0 auto;
405  padding: var(--space-2xl);
406  background: var(--color-surface);
407  border-radius: var(--border-radius-lg);
408  box-shadow: var(--shadow-lg);
409}
410
411.form-group {
412  margin-bottom: var(--space-lg);
413  position: relative;
414}
415
416.form-label {
417  display: block;
418  margin-bottom: var(--space-xs);
419  font-weight: 500;
420  color: var(--color-text);
421}
422
423.form-input,
424.form-textarea {
425  width: 100%;
426  padding: var(--space-md);
427  border: 2px solid var(--color-border);
428  border-radius: var(--border-radius);
429  background: var(--color-bg);
430  color: var(--color-text);
431  transition: border-color var(--transition-fast);
432
433  &:focus {
434    outline: none;
435    border-color: var(--color-primary);
436    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 20%, transparent);
437  }
438
439  &:invalid:not(:
440## Practical Application
441
442Practice this concept in the editor below.
443    border-color: #ef4444;
444  }
445
446  &:valid:not(:
447## Practical Application
448
449Practice this concept in the editor below.
450    border-color: #10b981;
451  }
452}
453
454.form-textarea {
455  min-height: 120px;
456  resize: vertical;
457}
458
459.form-error {
460  position: absolute;
461  bottom: -20px;
462  left: 0;
463  font-size: 0.875rem;
464  color: #ef4444;
465  opacity: 0;
466  transition: opacity var(--transition-fast);
467
468  &.visible {
469    opacity: 1;
470  }
471}
472
473/* ===== BUTTONS ===== */
474.btn {
475  display: inline-flex;
476  align-items: center;
477  justify-content: center;
478  padding: var(--space-md) var(--space-lg);
479  border: none;
480  border-radius: var(--border-radius);
481  font-weight: 500;
482  text-decoration: none;
483  cursor: pointer;
484  transition: all var(--transition-normal);
485  position: relative;
486  overflow: hidden;
487
488  &::before {
489    content: '';
490    position: absolute;
491    top: 0;
492    left: -100%;
493    width: 100%;
494    height: 100%;
495    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
496    transition: left var(--transition-slow);
497  }
498
499  &:hover::before {
500    left: 100%;
501  }
502
503  &--primary {
504    background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
505    color: white;
506
507    &:hover {
508      transform: translateY(-2px);
509      box-shadow: var(--shadow-lg);
510    }
511  }
512
513  &--secondary {
514    background: transparent;
515    color: var(--color-primary);
516    border: 2px solid var(--color-primary);
517
518    &:hover {
519      background: var(--color-primary);
520      color: white;
521    }
522  }
523
524  &--full {
525    width: 100%;
526  }
527}
528
529/* ===== ACCESSIBILITY ===== */
530@media (prefers-reduced-motion: reduce) {
531  *,
532  *::before,
533  *::after {
534    animation-duration: 0.01ms !important;
535    animation-iteration-count: 1 !important;
536    transition-duration: 0.01ms !important;
537    scroll-behavior: auto !important;
538  }
539}
540
541@media (prefers-color-scheme: dark) {
542  :root:not([data-theme="light"]) {
543    color-scheme: dark;
544  }
545}
546
547/* Focus styles */
548:focus-visible {
549  outline: 2px solid var(--color-primary);
550  outline-offset: 2px;
551}
552
553/* Skip link */
554.skip-link {
555  position: absolute;
556  top: -40px;
557  left: 6px;
558  background: var(--color-primary);
559  color: white;
560  padding: 8px;
561  text-decoration: none;
562  border-radius: 4px;
563  z-index: 1000;
564
565  &:focus {
566    top: 6px;
567  }
568}
569
570/* ===== UTILITY CLASSES ===== */
571@layer utilities {
572  .container {
573    max-width: var(--container-max-width);
574    margin: 0 auto;
575    padding: 0 var(--space-lg);
576  }
577
578  .section__title {
579    font-size: 2.5rem;
580    text-align: center;
581    margin-bottom: var(--space-2xl);
582    background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
583    background-clip: text;
584    -webkit-background-clip: text;
585    -webkit-text-fill-color: transparent;
586    color: transparent;
587  }
588
589  .visually-hidden {
590    position: absolute !important;
591    width: 1px !important;
592    height: 1px !important;
593    padding: 0 !important;
594    margin: -1px !important;
595    overflow: hidden !important;
596    clip: rect(0, 0, 0, 0) !important;
597    white-space: nowrap !important;
598    border: 0 !important;
599  }
600
601  .text-gradient {
602    background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
603    background-clip: text;
604    -webkit-background-clip: text;
605    -webkit-text-fill-color: transparent;
606  }
607}

3. JavaScript for Interactions

1// Theme toggle functionality
2class ThemeManager {
3  constructor() {
4    this.theme = localStorage.getItem('theme') || 'light';
5    this.toggle = document.querySelector('.theme-toggle');
6    this.init();
7  }
8
9  init() {
10    this.setTheme(this.theme);
11    this.toggle.addEventListener('click', () => this.toggleTheme());
12  }
13
14  setTheme(theme) {
15    document.documentElement.setAttribute('data-theme', theme);
16    this.toggle.querySelector('.theme-toggle__icon').textContent =
17      theme === 'dark' ? '☀️' : '🌙';
18    localStorage.setItem('theme', theme);
19    this.theme = theme;
20  }
21
22  toggleTheme() {
23    const newTheme = this.theme === 'light' ? 'dark' : 'light';
24    this.setTheme(newTheme);
25  }
26}
27
28// Form validation
29class FormValidator {
30  constructor(form) {
31    this.form = form;
32    this.fields = {
33      name: {
34        element: form.querySelector('#name'),
35        rules: ['required', 'minLength:2']
36      },
37      email: {
38        element: form.querySelector('#email'),
39        rules: ['required', 'email']
40      },
41      message: {
42        element: form.querySelector('#message'),
43        rules: ['required', 'minLength:10']
44      }
45    };
46    this.init();
47  }
48
49  init() {
50    Object.values(this.fields).forEach(field => {
51      field.element.addEventListener('blur', () => this.validateField(field));
52      field.element.addEventListener('input', () => this.clearError(field));
53    });
54
55    this.form.addEventListener('submit', (e) => this.handleSubmit(e));
56  }
57
58  validateField(field) {
59    const value = field.element.value.trim();
60    const errors = [];
61
62    field.rules.forEach(rule => {
63      if (rule === 'required' && !value) {
64        errors.push('This field is required');
65      } else if (rule === 'email' && value && !this.isValidEmail(value)) {
66        errors.push('Please enter a valid email address');
67      } else if (rule.startsWith('minLength:')) {
68        const minLength = parseInt(rule.split(':')[1]);
69        if (value && value.length < minLength) {
70          errors.push(`Minimum ${minLength} characters`);
71        }
72      }
73    });
74
75    this.showError(field, errors[0]);
76    return errors.length === 0;
77  }
78
79  showError(field, message) {
80    const errorElement = document.querySelector(`#${field.element.name}-error`);
81    if (message) {
82      errorElement.textContent = message;
83      errorElement.classList.add('visible');
84      field.element.setAttribute('aria-invalid', 'true');
85    } else {
86      this.clearError(field);
87    }
88  }
89
90  clearError(field) {
91    const errorElement = document.querySelector(`#${field.element.name}-error`);
92    errorElement.classList.remove('visible');
93    field.element.removeAttribute('aria-invalid');
94  }
95
96  isValidEmail(email) {
97    return /^[^s@]+@[^s@]+\.[^s@]+$/.test(email);
98  }
99
100  handleSubmit(e) {
101    e.preventDefault();
102
103    const isValid = Object.values(this.fields)
104      .map(field => this.validateField(field))
105      .every(valid => valid);
106
107    if (isValid) {
108      this.submitForm();
109    }
110  }
111
112  async submitForm() {
113    const submitButton = this.form.querySelector('button[type="submit"]');
114    const originalText = submitButton.textContent;
115
116    submitButton.textContent = 'Sending...';
117    submitButton.disabled = true;
118
119    try {
120      // Simulating sending
121      await new Promise(resolve => setTimeout(resolve, 2000));
122
123      // Success message
124      this.showSuccessMessage();
125      this.form.reset();
126    } catch (error) {
127      alert('An error occurred while sending the message');
128    } finally {
129      submitButton.textContent = originalText;
130      submitButton.disabled = false;
131    }
132  }
133
134  showSuccessMessage() {
135    const message = document.createElement('div');
136    message.className = 'success-message';
137    message.textContent = 'Message sent successfully!';
138    message.style.cssText = `
139      position: fixed;
140      top: 20px;
141      right: 20px;
142      background: #10b981;
143      color: white;
144      padding: 1rem 1.5rem;
145      border-radius: 0.5rem;
146      box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1);
147      z-index: 1000;
148      animation: slideIn 0.3s ease;
149    `;
150
151    document.body.appendChild(message);
152
153    setTimeout(() => {
154      message.style.animation = 'slideOut 0.3s ease';
155      setTimeout(() => message.remove(), 300);
156    }, 3000);
157  }
158}
159
160// Smooth scrolling for navigation
161class SmoothScrolling {
162  constructor() {
163    this.init();
164  }
165
166  init() {
167    document.querySelectorAll('a[href^="#"]').forEach(link => {
168      link.addEventListener('click', (e) => {
169        e.preventDefault();
170        const target = document.querySelector(link.getAttribute('href'));
171        if (target) {
172          target.scrollIntoView({
173            behavior: 'smooth',
174            block: 'start'
175          });
176        }
177      });
178    });
179  }
180}
181
182// Skills animation
183class SkillsAnimation {
184  constructor() {
185    this.init();
186  }
187
188  init() {
189    if ('IntersectionObserver' in window) {
190      this.observeSkills();
191    } else {
192      this.animateSkillsDirectly();
193    }
194  }
195
196  observeSkills() {
197    const observer = new IntersectionObserver((entries) => {
198      entries.forEach(entry => {
199        if (entry.isIntersecting) {
200          this.animateSkill(entry.target);
201          observer.unobserve(entry.target);
202        }
203      });
204    }, { threshold: 0.5 });
205
206    document.querySelectorAll('.skill-item__progress').forEach(skill => {
207      observer.observe(skill);
208    });
209  }
210
211  animateSkill(skillElement) {
212    const level = skillElement.dataset.level;
213    skillElement.style.transform = `scaleX(${level / 100})`;
214  }
215
216  animateSkillsDirectly() {
217    document.querySelectorAll('.skill-item__progress').forEach(skill => {
218      setTimeout(() => this.animateSkill(skill), 500);
219    });
220  }
221}
222
223// Initialize everything when DOM is loaded
224document.addEventListener('DOMContentLoaded', () => {
225  new ThemeManager();
226  new SmoothScrolling();
227  new SkillsAnimation();
228
229  const contactForm = document.querySelector('.contact-form');
230  if (contactForm) {
231    new FormValidator(contactForm);
232  }
233
234  // Add success animations
235  const style = document.createElement('style');
236  style.textContent = `
237    @keyframes slideIn {
238      from { transform: translateX(100%); opacity: 0; }
239      to { transform: translateX(0); opacity: 1; }
240    }
241    @keyframes slideOut {
242      from { transform: translateX(0); opacity: 1; }
243      to { transform: translateX(100%); opacity: 0; }
244    }
245  `;
246  document.head.appendChild(style);
247});

Technical Requirements

1. Use All Learned Techniques

  • Container Queries - responsive project grid
  • CSS Nesting - CSS code organization
  • CSS Variables - design token system
  • Cascade Layers - cascade management
  • Web Components styling - UI components
  • WCAG compliance - accessibility
  • BEM methodology - class naming
  • Stylelint - CSS code quality
  • Cross-browser support - compatibility

2. Performance Optimization

1/* Critical CSS inlining */
2/* Only the most important styles in <head> */
3
4/* Lazy loading images */
5img[loading="lazy"] {
6  content-visibility: auto;
7  contain-intrinsic-size: 200px 150px;
8}
9
10/* CSS containment */
11.project-card {
12  contain: layout style paint;
13}
14
15/* Preload important resources */
16/* <link rel="preload" href="fonts/inter.woff2" as="font" type="font/woff2" crossorigin> */

3. Testing Checklist

  • [ ] Responsiveness on all devices
  • [ ] Keyboard accessibility
  • [ ] Screen reader compatibility
  • [ ] Performance (Lighthouse score > 90)
  • [ ] Cross-browser testing (Chrome, Firefox, Safari, Edge)
  • [ ] Dark/light mode functionality
  • [ ] Form validation
  • [ ] Smooth animations

Additional Challenges

Advanced Level

  1. View Transitions API - add smooth transitions between sections
  2. CSS Scroll Snap - snap scrolling between sections
  3. CSS @property - animated custom properties
  4. Intersection Observer - lazy loading and animations
  5. Service Worker - caching for offline functionality

Expert Level

  1. CSS Houdini Paint API - custom gradients
  2. CSS Parser API - dynamic styles
  3. Web Animations API - advanced JS animations
  4. CSS Typed OM - programmatic CSS manipulation

Project Documentation

README.md

1# Portfolio Website - Modern CSS Techniques
2
3Responsive portfolio page utilizing the latest CSS techniques.
4
5## Technologies Used
6
7- HTML5 Semantic markup
8- CSS3 with latest features
9- Vanilla JavaScript ES6+
10- Container Queries
11- CSS Nesting
12- Cascade Layers
13- CSS Grid & Subgrid
14
15## Installation
16
171. Clone the repository
182. Open index.html in a browser
193. Run a local server for full functionality
20
21## Features
22
23- Responsive design with Container Queries
24- Dark/Light mode toggle
25- Smooth scroll animations
26- Form validation
27- WCAG 2.1 AA compliance
28- Cross-browser compatibility
29
30## Performance
31
32- Lighthouse Performance: 95+
33- First Contentful Paint: < 1.5s
34- Largest Contentful Paint: < 2.5s
35- Cumulative Layout Shift: < 0.1

Project Summary

This project demonstrates the practical application of all advanced CSS techniques learned in this module:

  1. CSS Architecture - Cascade Layers, CSS Nesting, BEM
  2. Modern Layout - Container Queries, CSS Grid, Subgrid
  3. Advanced Styling - CSS Variables, custom properties
  4. Interactions - animations, hover effects, scroll-driven animations
  5. Accessibility - WCAG compliance, focus management
  6. Performance - CSS containment, lazy loading
  7. Cross-browser - progressive enhancement, fallbacks

The project can be expanded with additional features and serves as an excellent foundation for further CSS skill development.

Go to CodeWorlds