طراحی وب با HTML و CSS فهرست مطالب
فصل ۱۳

تکنیک‌های تکمیلی CSS

هر چیزی که برای ساختن یک سایت حرفه‌ای لازم است و تا اینجا نگفته بودیم. این فصل، پل ارتباطی بین دانش پایه شما و پروژه نهایی فصل بعد است.

در این فصل با این مباحث آشنا می‌شوید
  1. متغیرهای CSS
  2. سایه
  3. گرادیانت و شفافیت
  4. transform
  5. object-fit و calc
  6. شبه‌کلاس‌های پیشرفته
  7. شبه‌عنصرها
  8. نکته‌های تکمیلی
  9. نمونه نهایی فصل

۱. متغیرهای CSS

تا اینجا هر بار رنگ سایت را دستی نوشتیم. اگر بخواهید رنگ اصلی سایتتان را عوض کنید، باید ده‌ها جا را تغییر دهید و حتماً یکی دو مورد را فراموش می‌کنید. متغیر این مشکل را کاملاً حل می‌کند.

:root {
    --primary: #2f4f4f;
    --primary-dark: #223a3a;
    --accent: #ff6347;
    --bg-light: #f5f5f5;
    --text: #333333;
    --muted: #777777;
    --border: #e0e0e0;
    --radius: 10px;
    --space: 20px;
}

body {
    background-color: var(--bg-light);
    color: var(--text);
}

.btn {
    background-color: var(--primary);
    border-radius: var(--radius);
    padding: 12px var(--space);
}

.btn:hover {
    background-color: var(--accent);
}

متغیرها: نمونه 01-css-variables

شرح مثال

حالا برای عوض کردن رنگ اصلی کل سایت، تنها یک خط در بالای فایل تغییر می‌کند و همه جا با هم به‌روز می‌شود.

چه چیزهایی را متغیر کنیم

هر چیزی که بیش از دو بار تکرار می‌شود: رنگ‌ها، گردی گوشه‌ها، فاصله استاندارد، سایه‌ها. اگر عددی تنها یک بار در کل فایل استفاده می‌شود، نیازی به متغیر ندارد.

۲. سایه

سایه به عنصرها عمق می‌دهد و باعث می‌شود کارت‌ها انگار روی صفحه شناور باشند. این ساده‌ترین راه حرفه‌ای‌تر کردن یک طرح است.

.card {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.card-hover {
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}

.inner {
    box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.2);
}

.title {
    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}

سایه: نمونه 02-shadow

شرح مثال

box-shadow چهار مقدار می‌گیرد:

مقدارمعنی
اولجابه‌جایی افقی سایه
دومجابه‌جایی عمودی. مثبت یعنی سایه به پایین.
سوممیزان محو شدن. عدد بزرگ‌تر یعنی سایه نرم‌تر.
چهارمرنگ سایه

کلمه inset در ابتدا، سایه را به داخل جعبه می‌برد و حس فرورفتگی می‌دهد. text-shadow هم دقیقاً همین‌طور کار می‌کند اما برای متن، و مقدار inset ندارد.

راز سایه‌های خوب

سایه‌های زیبا ملایم هستند. از رنگ سیاه با شفافیت کم استفاده کنید، نه از خاکستری توپر. rgba(0, 0, 0, 0.1) یعنی سیاه با ده درصد ماندگاری، که زیر آن هر رنگی باشد طبیعی به نظر می‌رسد. سایه‌های تیره و تند، طرح را قدیمی نشان می‌دهند.

۳. گرادیانت و شفافیت

.gradient {
    background-image: linear-gradient(to left, #2f4f4f, #ff6347);
}

.faded {
    opacity: 0.5;
}

.overlay-photo {
    background-image:
        linear-gradient(rgba(20, 40, 40, 0.7), rgba(20, 40, 40, 0.7)),
        url('images/school.jpg');
    background-size: cover;
    background-position: center;
    color: white;
}

گرادیانت و شفافیت: نمونه 03-gradient-opacity

شرح مثال

linear-gradient یک تصویر می‌سازد که از یک رنگ به رنگ دیگر محو می‌شود. اولین مقدار جهت است و بقیه رنگ‌ها. چون یک تصویر است، در background-image نوشته می‌شود، نه در background-color.

opacity شفافیت کل عنصر را تنظیم می‌کند: عدد ۱ یعنی کاملاً مات و ۰ یعنی کاملاً نامرئی. نکته مهم اینکه این ویژگی روی همه چیز اثر می‌گذارد، از جمله متن داخل عنصر. اگر تنها پس‌زمینه را می‌خواهید شفاف کنید، از rgba استفاده کنید.

rgba همان rgb است با یک عدد چهارم: میزان ماندگاری از صفر تا یک. پس rgba(0, 0, 0, 0.5) یعنی سیاه نیمه‌شفاف.

مهم‌ترین تکنیک این بخش

بخش سوم مثال، تکنیکی است که تقریباً هر سایتی از آن استفاده می‌کند: پرده تیره روی عکس. یک گرادیانت که هر دو رنگش یکسان و نیمه‌شفاف است، مانند یک شیشه دودی روی عکس عمل می‌کند. نتیجه‌اش این است که متن سفید روی هر عکسی خوانا می‌ماند. دقت کنید که گرادیانت قبل از عکس نوشته می‌شود، چون لایه‌های پس‌زمینه از بالا به پایین چیده می‌شوند. در فصل بعد دقیقاً از همین کد برای هدر پروژه استفاده می‌کنیم.

۴. transform

.move {
    transform: translateY(-5px);
}

.grow {
    transform: scale(1.05);
}

.rotate {
    transform: rotate(5deg);
}

.combined {
    transform: translateY(-5px) scale(1.03);
}

.card {
    transition: transform 0.3s ease;
}

.card:hover {
    transform: translateY(-8px);
}

transform: نمونه 04-transform

شرح مثال

تابعکارش
translateX و translateYجابه‌جایی افقی و عمودی
scaleبزرگ یا کوچک کردن. عدد ۱ یعنی اندازه اصلی.
rotateچرخاندن بر حسب درجه

می‌شود چند تابع را با فاصله پشت سر هم نوشت و همه با هم اعمال می‌شوند. اما دقت کنید که نمی‌توانید دو خط transform جدا بنویسید؛ دومی اولی را پاک می‌کند.

چرا transform بهتر از تغییر مستقیم است

برای بالا بردن یک کارت می‌شد margin-top منفی هم داد، اما آن کار باعث می‌شود بقیه عنصرهای صفحه جابه‌جا شوند و چیدمان تکان بخورد. transform تنها نمایش عنصر را عوض می‌کند و جای واقعی‌اش دست‌نخورده می‌ماند. به‌علاوه مرورگر برای آن از کارت گرافیک استفاده می‌کند و حرکت بسیار نرم‌تر است.

transform تقریباً همیشه همراه transition و hover: می‌آید. سه خط آخر مثال، پرکاربردترین الگوی افکت کارت در کل طراحی وب است.

۵. object-fit و calc

.card-image {
    width: 100%;
    height: 200px;
    object-fit: cover;
}

.logo {
    width: 100px;
    height: 100px;
    object-fit: contain;
}

.full-bleed {
    width: calc(100% + 40px);
    margin-right: -20px;
    margin-left: -20px;
}

.main-area {
    width: calc(100% - 250px);
}

object-fit و calc: نمونه 05-object-fit-calc

شرح مثال

مشکل: در یک ردیف کارت، عکس‌ها اندازه‌های مختلفی دارند و کارت‌ها نامرتب می‌شوند. اگر عرض و ارتفاع ثابت بدهید، عکس‌ها کشیده و بدشکل می‌شوند.

راه‌حل: object-fit که دقیقاً مانند background-size عمل می‌کند، اما روی تگ img:

calc() اجازه می‌دهد داخل CSS محاسبه کنید، حتی بین واحدهای متفاوت. این کاری است که هیچ راه دیگری برای انجامش نیست: مرورگر نمی‌داند صد درصد چند پیکسل است، اما calc در لحظه نمایش حسابش می‌کند.

فاصله دور علامت‌ها اجباری است

calc(100% - 250px) درست است، اما calc(100%-250px) اصلاً کار نمی‌کند. دور علامت‌های جمع و تفریق حتماً باید فاصله باشد.

ترکیب calc با حاشیه منفی، تکنیک «تصویر تمام‌عرض کارت» را می‌سازد: تصویر از لبه تا لبه کارت کشیده می‌شود و padding کارت را نادیده می‌گیرد.

۶. شبه‌کلاس‌های پیشرفته

تا اینجا تنها hover: و focus: را دیدیم. چند شبه‌کلاس دیگر هم هستند که بر اساس جایگاه عنصر انتخاب می‌کنند.

li:first-child {
    font-weight: bold;
}

li:last-child {
    border-bottom: none;
}

tr:nth-child(odd) {
    background-color: #f7f7f7;
}

tr:nth-child(even) {
    background-color: white;
}

li:nth-child(3) {
    color: #ff6347;
}

input:focus {
    border-color: #2f4f4f;
}

.btn:active {
    transform: scale(0.97);
}

شبه‌کلاس‌های پیشرفته: نمونه 06-pseudo-class-advanced

شرح مثال

شبه‌کلاسانتخاب می‌کند
:first-childاولین فرزند والدش
:last-childآخرین فرزند
:nth-child(odd)فرزندان فرد: اول، سوم، پنجم
:nth-child(even)فرزندان زوج
:nth-child(3)دقیقاً سومی
:focusعنصری که نشانگر داخلش است
:activeلحظه‌ای که کاربر کلیک را نگه داشته

nth-child(odd): پرکاربردترینشان است و جدول راه‌راه می‌سازد که خواندن سطرهایش بسیار آسان‌تر است. last-child: هم برای برداشتن خط جداکننده آخرین مورد یک فهرست عالی است، تا خط اضافه‌ای زیر فهرست نماند.

active: حس فشرده شدن دکمه را می‌دهد. کوچک کردن بسیار جزئی دکمه در لحظه کلیک، بازخوردی است که کاربر آن را نمی‌بیند اما حس می‌کند.

۷. شبه‌عنصرها

شبه‌کلاس یک عنصر موجود را انتخاب می‌کند. شبه‌عنصر یک قطعه تازه می‌سازد که در HTML وجود ندارد. علامتش دو تا دو نقطه است.

.section-title {
    position: relative;
    padding-bottom: 12px;
}

.section-title::after {
    content: "";
    position: absolute;
    right: 0;
    bottom: 0;
    width: 60px;
    height: 4px;
    background-color: var(--accent);
    border-radius: 2px;
}

.new-card::before {
    content: "جدید";
    position: absolute;
    top: 10px;
    left: 10px;
    background-color: var(--accent);
    color: white;
    padding: 4px 12px;
    border-radius: 20px;
    font-size: 13px;
}

.check-list li::before {
    content: "✔ ";
    color: green;
}

شبه‌عنصرها: نمونه 07-pseudo-element

شرح مثال

چرا این مهم است

آن خط تزیینی نارنجی زیر عنوان‌ها، بدون شبه‌عنصر نیاز به یک div خالی و بی‌معنی در HTML داشت. با after:: عنصر تزیینی در جای درست خودش یعنی CSS می‌ماند و HTML تمیز باقی می‌ماند. قانون کلی این است: هر چیزی که تنها تزیین است، در CSS ساخته شود نه در HTML.

۸. نکته‌های تکمیلی

<a href="#contact">تماس با ما</a>
<section id="contact">...</section>

<a href="mailto:info@school.ir">ایمیل بزنید</a>
<a href="tel:01133445566">تماس بگیرید</a>
<a href="https://kadoosedu.ir" target="_blank">سایت کادوس</a>
html {
    scroll-behavior: smooth;
}

.clickable {
    cursor: pointer;
}

.gray-photo {
    filter: grayscale(100%);
    transition: filter 0.3s;
}

.gray-photo:hover {
    filter: grayscale(0);
}

.blurred {
    filter: blur(3px);
}

نکته‌های تکمیلی: نمونه 08-extra-touches

شرح مثال

دو تگ فراموش‌شده در head

<title> عنوان سربرگ مرورگر است و در نتایج گوگل هم به عنوان تیتر آبی نمایش داده می‌شود. <meta name="description"> هم آن متن توضیحی زیر تیتر است. هر صفحه باید هر دو را داشته باشد و محتوایشان باید برای هر صفحه متفاوت باشد.

۹. نمونه نهایی فصل

<body>

    <header class="hero">
        <h1>دبیرستان دانش</h1>
        <a href="#courses" class="btn">رشته‌ها را ببینید</a>
    </header>

    <section id="courses">
        <h2 class="section-title">رشته‌های تحصیلی</h2>

        <div class="cards">
            <div class="card new-card">
                <img src="images/math.jpg" alt="ریاضی">
                <h3>ریاضی و فیزیک</h3>
            </div>
            <div class="card">
                <img src="images/lab.jpg" alt="آزمایشگاه">
                <h3>علوم تجربی</h3>
            </div>
        </div>

        <ul class="check-list">
            <li>آزمایشگاه مجهز</li>
            <li>کتابخانه بزرگ</li>
            <li>کلاس‌های کم‌جمعیت</li>
        </ul>
    </section>

</body>
:root {
    --primary: #2f4f4f;
    --accent: #ff6347;
    --bg-light: #f5f5f5;
    --text: #333333;
    --radius: 10px;
    --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.08);
    --shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.15);
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: Tahoma, sans-serif;
}

html {
    scroll-behavior: smooth;
}

body {
    background-color: var(--bg-light);
    color: var(--text);
    line-height: 1.9;
}

/* هدر بزرگ با پرده تیره روی عکس */
.hero {
    height: 60vh;
    background-image:
        linear-gradient(rgba(20, 40, 40, 0.7), rgba(20, 40, 40, 0.7)),
        url('images/school.jpg');
    background-size: cover;
    background-position: center;
    color: white;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 20px;
    text-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}

.btn {
    background-color: var(--accent);
    color: white;
    text-decoration: none;
    padding: 14px 32px;
    border-radius: var(--radius);
    cursor: pointer;
    transition: transform 0.3s ease;
}

.btn:hover {
    transform: scale(1.05);
}

.btn:active {
    transform: scale(0.97);
}

section {
    padding: 50px 40px;
}

/* عنوان بخش با خط تزیینی */
.section-title {
    position: relative;
    color: var(--primary);
    padding-bottom: 12px;
    margin-bottom: 30px;
}

.section-title::after {
    content: "";
    position: absolute;
    right: 0;
    bottom: 0;
    width: 60px;
    height: 4px;
    background-color: var(--accent);
    border-radius: 2px;
}

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 20px;
    margin-bottom: 35px;
}

.card {
    position: relative;
    background-color: white;
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
    transform: translateY(-8px);
    box-shadow: var(--shadow-lg);
}

.card img {
    width: 100%;
    height: 190px;
    object-fit: cover;
    display: block;
    filter: grayscale(40%);
    transition: filter 0.3s;
}

.card:hover img {
    filter: grayscale(0);
}

.card h3 {
    color: var(--primary);
    padding: 15px 18px;
}

/* نشان جدید روی کارت */
.new-card::before {
    content: "جدید";
    position: absolute;
    top: 12px;
    left: 12px;
    z-index: 5;
    background-color: var(--accent);
    color: white;
    padding: 4px 14px;
    border-radius: 20px;
    font-size: 13px;
}

/* فهرست با تیک */
.check-list {
    list-style: none;
    background-color: white;
    padding: 20px 25px;
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}

.check-list li {
    padding: 10px 0;
    border-bottom: 1px solid #eeeeee;
}

.check-list li::before {
    content: "✔ ";
    color: var(--accent);
    font-weight: bold;
}

.check-list li:last-child {
    border-bottom: none;
}

نمونه نهایی فصل: نمونه 09-final-example

شرح مثال

این صفحه تقریباً هر چیزی را که در این فصل خواندیم به کار می‌برد و شکل و حال یک سایت واقعی و امروزی را دارد.

یک نکته تازه در گرید

در بخش کارت‌ها نوشته‌ایم repeat(auto-fit, minmax(240px, 1fr)). معنی‌اش این است: «هر چند ستون که جا می‌شود بساز، به شرطی که هیچ ستونی باریک‌تر از ۲۴۰ پیکسل نشود». نتیجه‌اش یک گالری کاملاً واکنش‌گراست، بدون حتی یک media query. این پیشرفته‌ترین نکته این کتاب است و اگر آن را به خاطر بسپارید، در پروژه‌های واقعی بسیار به کارتان می‌آید.

فایل‌های تمرینی این فصل

پوشه 13-advanced-css شامل نمونه‌های 01-css-variables تا 09-final-example.

خلاصه فصل

ویژگیکارش
:root و --nameتعریف متغیر
var(--name)استفاده از متغیر
box-shadowسایه جعبه. با inset به داخل.
text-shadowسایه متن
linear-gradientمحو شدن یک رنگ در رنگ دیگر
rgbaرنگ با شفافیت
opacityشفافیت کل عنصر، شامل متنش
transformجابه‌جایی، بزرگ‌نمایی، چرخش
object-fit: coverهم‌اندازه کردن تصویرها بدون کشیدگی
calc()محاسبه بین واحدهای متفاوت
:first-child :last-childاولین و آخرین فرزند
:nth-child(odd)فرزندان فرد، برای جدول راه‌راه
:activeلحظه فشرده شدن
::before و ::afterساختن قطعه تزیینی. content اجباری است.
scroll-behavior: smoothاسکرول نرم برای لینک‌های داخلی
filterجلوه‌هایی مانند سیاه‌وسفید و محو

تمرین‌های فصل سیزده

  1. فایل CSS یکی از تمرین‌های قبلی را بردارید و همه رنگ‌هایش را به متغیر تبدیل کنید. بعد با عوض کردن یک خط، تم کل صفحه را تغییر دهید.
  2. یک هدر بزرگ با عکس و پرده تیره بسازید که متن سفیدش کاملاً خوانا باشد.
  3. کارتی بسازید که با رفتن موس روی آن، بالا بیاید و سایه‌اش بزرگ‌تر شود.
  4. زیر همه عنوان‌های بخش، یک خط تزیینی با after:: اضافه کنید.
  5. یک جدول راه‌راه با nth-child(odd): بسازید.
  6. یک گالری با repeat(auto-fit, minmax(240px, 1fr)) بسازید و پنجره را باریک کنید تا رفتارش را ببینید.
  7. تصویرهایی با اندازه‌های مختلف در کارت‌ها بگذارید و با object-fit: cover مرتبشان کنید.
فصل قبل: طراحی واکنش‌گرا فصل بعد: پروژه نهایی