تاروپود — خودآموز HTML و CSS

فصل ۱۰ از ۱۰

پیشرفت ترم
۰٪

ترم ۵ · تعامل و جزئیات

پروژه: یک component تعاملی بدون JS

فصل ۱۰پیش‌نمایش رایگان
۷ دقیقه مطالعه فصل ۱۰

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

این آخرین فصلِ ترمِ «تعامل و جزئیات» است و مفهومِ تازه‌ای ندارد. کارِ امروز ترکیب است: یک آکاردئون می‌سازی — یک فهرست از پرسش‌ها که هر کدام با یک کلیک باز و بسته می‌شوند — و همه‌اش بدونِ یک خط JavaScript. هر ابزاری که این ترم ساختی اینجا نقشی دارد: :checked برای حالت، :has() برای واکنشِ والد، ::after برای علامتِ باز/بسته، transform و transition برای چرخشِ نرمِ آن علامت، :hover و :focus-visible برای واکنش، و از همه مهم‌تر، یک تعاملِ قابلِ استفاده با صفحه‌کلید.

یک آکاردئونِ سه‌ردیفی: ردیفِ بالا باز است و متنِ پاسخ زیرش دیده می‌شود با علامتِ ضربدر، دو ردیفِ دیگر بسته‌اند با علامتِ به‌علاوه

آخر این فصل می‌توانی:

  • یک آکاردئونِ کاملاً بی‌جاوااسکریپت با :checked و :has() بسازی
  • علامتِ باز/بسته را با ::after و transform نرم بچرخانی
  • یک checkbox را طوری پنهان کنی که دیده نشود ولی با صفحه‌کلید هنوز کار کند

قبل از شروع#

  • از فصل ۵: :checked. از فصل ۹: :has(). از فصل ۳: ::after و content.
  • از فصل ۱: :hover و :focus-visible. از فصل ۶: rotate. از فصل ۷: transition.

فقط یک مرورگر لازم داری، یا همین آزمایشگاه.

۱. اسکلت: هر ردیف یک checkbox پنهان دارد#

هر ردیف سه چیز دارد: یک <input type="checkbox"> که حالتِ باز/بسته را نگه می‌دارد، یک <label> که همان سؤال است و با کلیک آن checkbox را می‌زند، و یک پنلِ پاسخ. label با for به checkbox وصل است، پس کلیک روی سؤال، تیک را می‌زند — همان چیزی که در فصل ۵ دیدی.

۲. باز و بسته با :has()#

پنل پیش‌فرض بسته است (max-height: 0 و overflow: hidden). حالا با :has() از فصل ۹ می‌گوییم: «ردیفی که checkbox اش تیک خورده، پنلش باز شود». چون از فصل ۷ روی پنل transition گذاشته‌ایم، این باز شدن نرم است، نه ناگهانی. علامتِ + هم که با ::after ساخته‌ایم، با rotate(45deg) به یک × می‌چرخد تا بگوید «حالا بازم، دوباره بزن تا ببندم».

۳. اشتباهِ رایج: پنهان‌کردنی که صفحه‌کلید را می‌کُشد#

checkbox واقعی زشت است و می‌خواهیم پنهانش کنیم. ساده‌ترین راهی که به ذهن می‌رسد این است:

.q-input {
  display: none;
}

با ماوس همه‌چیز عالی به‌نظر می‌رسد: روی سؤال کلیک می‌کنی، باز و بسته می‌شود. ولی یک آزمایش کن: ماوس را کنار بگذار و فقط با کلیدِ Tab روی صفحه بگرد. به هیچ سؤالی نمی‌رسی، و با صفحه‌کلید هیچ‌کدام باز نمی‌شوند. برای کسی که ماوس ندارد، این آکاردئون کاملاً مرده است.

🔧 اگر کار نکرد: display: none فقط چیزی را پنهان نمی‌کند — آن را از ترتیبِ Tab و از دسترسِ صفحه‌کلید هم حذف می‌کند. راهِ درست این است که checkbox را طوری پنهان کنی که دیده نشود ولی هنوز focusable بماند: با position: absolute از جریان درش بیاور و با opacity: 0 و اندازهٔ یک‌پیکسلی نامرئی‌اش کن. حالا هنوز آنجاست، فقط دیده نمی‌شود:

.q-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

بعد با :focus-visible (فصل ۱) و :has() یک حلقهٔ focus روی خودِ سؤال نشان می‌دهیم تا کاربرِ صفحه‌کلید ببیند الان روی کدام ردیف است. حالا هم ماوس کار می‌کند، هم Tab و Space.

چک کن: با ماوس ردیف‌ها باز و بسته می‌شوند و علامتِ + نرم به × می‌چرخد. حالا Tab بزن: روی هر سؤال یک حلقهٔ آبی می‌آید، و با Space همان ردیف باز می‌شود. هر دو راه کار می‌کنند.

۴. کد کاملِ پروژه#

اول index.html:

<!-- index.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>آکاردئون بدون JavaScript</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <main class="page">
      <section class="faq">
        <h1>پرسش‌های پرتکرار</h1>
        <ul class="faq-list">
          <li class="item">
            <input class="q-input" type="checkbox" id="q1">
            <label class="q-label" for="q1">CSS بدون JavaScript چقدر تعاملی می‌شود؟</label>
            <p class="panel">خیلی بیشتر از چیزی که فکر می‌کنی — همین آکاردئون مثالش است.</p>
          </li>
          <li class="item">
            <input class="q-input" type="checkbox" id="q2">
            <label class="q-label" for="q2">چرا از checkbox استفاده کردیم؟</label>
            <p class="panel">چون checkbox دو حالت دارد و مرورگر خودش این حالت را نگه می‌دارد.</p>
          </li>
          <li class="item">
            <input class="q-input" type="checkbox" id="q3">
            <label class="q-label" for="q3">با صفحه‌کلید هم باز می‌شود؟</label>
            <p class="panel">اگر checkbox را درست پنهان کنیم، بله: با Tab می‌رسی و با Space بازش می‌کنی.</p>
          </li>
        </ul>
      </section>
    </main>
  </body>
</html>

و styles.css:

/* styles.css */
* {
  box-sizing: border-box;
}

:root {
  --bg: #f6edda;
  --card: #ffffff;
  --ink: #2c2114;
  --muted: #6b6150;
  --brand: #2a6b93;
  --line: #d8ccae;
  --open: #eaf1f6;
}

body {
  margin: 0;
  font-family: Tahoma, sans-serif;
  background-color: var(--bg);
  color: var(--ink);
  padding: 40px 20px;
  line-height: 1.9;
}

.faq {
  max-width: 460px;
  margin-inline: auto;
}

.faq h1 {
  margin-block: 0 16px;
  font-size: 1.5rem;
}

.faq-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.item {
  background-color: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  margin-block-end: 10px;
  overflow: hidden;
}

/* checkbox penhan ast vali focusable mimanad */
.q-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.q-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 14px 16px;
  cursor: pointer;
  font-weight: 700;
  transition: background-color 0.2s;
}

.q-label:hover {
  background-color: var(--open);
}

/* alamat-e + ba ::after (fasl 3), ba charkhesh-e narm (fasl 6 + 7) */
.q-label::after {
  content: "+";
  color: var(--brand);
  font-size: 1.4rem;
  line-height: 1;
  transition: transform 0.2s;
}

.panel {
  max-height: 0;
  margin: 0;
  padding: 0 16px;
  overflow: hidden;
  color: var(--muted);
  transition: max-height 0.25s ease, padding 0.25s ease;
}

/* :has() -> vaghti item baz ast (checkbox checked): panel baz shavad */
.item:has(.q-input:checked) .panel {
  max-height: 200px;
  padding-block: 8px 16px;
}

.item:has(.q-input:checked) .q-label {
  background-color: var(--open);
}

/* + -> x */
.item:has(.q-input:checked) .q-label::after {
  transform: rotate(45deg);
}

/* focus-visible ba safhe-kelid: halghe rooye label */
.item:has(.q-input:focus-visible) .q-label {
  outline: 3px solid var(--brand);
  outline-offset: -3px;
}

نمای نزدیک از یک ردیفِ باز: نوارِ سؤالِ رنگی با علامتِ ضربدر، پاسخ زیرش، و یک حلقهٔ آبیِ focus دورِ نوار که نشانِ رسیدن با صفحه‌کلید است

چک کن: سه ردیف داری که مستقل باز و بسته می‌شوند، علامتشان نرم می‌چرخد، با ماوس و با صفحه‌کلید هر دو کار می‌کنند — و در کلِ فایل یک خط JavaScript نیست. این کلِ ترمِ ۵ در یک component است.

🤖 با AI: آکاردئونِ کاملت را به دستیار AI ات بده و بپرس «این از نظرِ دسترس‌پذیری و کار با صفحه‌کلید چه ضعفی دارد؟». جوابش را یک فهرستِ کارها ببین، نه حکمِ نهایی — هر موردش را همان‌جا در آزمایشگاه با Tab و Space خودت چک کن.

واژه‌های تازهٔ این فصل#

این فصل یک فصلِ پروژه‌ای است و واژهٔ فنیِ تازه‌ای ندارد؛ فقط ابزارهای این ترم — :checked، :has()، ::after، transform، transition و :focus-visible — را کنارِ هم به‌کار می‌گیرد.

تمرین‌ها

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

در فصل بعد#

اینجا آخرِ ترمِ «تعامل و جزئیات» است. حالا می‌توانی صفحه را با حالت‌ها، pseudo-element، فرمِ استایل‌خورده، transform، animation و :has() زنده و تعاملی کنی — همه بدونِ JavaScript. تا اینجا هر فصل یک تکه بود؛ ترمِ بعد — حرفه‌ای و واقعی — یاد می‌گیری این تکه‌ها را مثلِ یک حرفه‌ای سازماندهی کنی: CSS را تمیز و مقیاس‌پذیر بنویسی، مثلِ component فکر کنی، یک design system کوچکِ خودت بسازی، دسترس‌پذیری را عمیق‌تر بگیری، و بالاخره یک سایتِ چندصفحه‌ایِ واقعی را روی اینترنت منتشر کنی.

به آخر این فصل رسیدی!

اگر ساختی و جواب داد، این دکمه مال توست.