React এর হাতেখড়ি (পর্ব ১৬) — React-এ Styling শিখুন

React-এ Styling - CSS Modules, inline style, className

আগের পর্বে React Router শিখেছিলাম। এই পর্বে আমরা দেখব React-এ Styling-এর বিভিন্ন উপায় — এতদিন আমরা শুধু inline style অবজেক্ট ব্যবহার করেছি (পর্ব ২-এ শেখা), এবার CSS Modules আর conditional className-সহ পুরো চিত্রটা দেখব।

React-এ স্টাইলিংয়ের চারটা মূল উপায়

পদ্ধতি সংক্ষেপে
সাধারণ CSS ফাইল Advanced CSS সিরিজের মতোই, সব ক্লাস গ্লোবাল
Inline Style JavaScript অবজেক্ট, ডাইনামিক মানের জন্য উপযুক্ত
CSS Modules প্রতিটা কম্পোনেন্টের CSS স্বয়ংক্রিয়ভাবে "স্কোপড", ক্লাস নাম সংঘর্ষ হয় না
CSS-in-JS লাইব্রেরি styled-components ইত্যাদি — এই সিরিজের বাইরে

পদ্ধতি ১ — সাধারণ CSS ফাইল

একটা সাধারণ .css ফাইল বানিয়ে সরাসরি import করা যায় — Advanced CSS সিরিজে শেখা সবকিছুই এখানে কাজ করে (BEM, Custom Properties, সবকিছু)।

/* Card.css */
.card {
  background: white;
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.card__title {
  font-size: 18px;
  font-weight: bold;
}
// Card.jsx
import "./Card.css";

function Card({ title }) {
  return (
    <div className="card">
      <h3 className="card__title">{title}</h3>
    </div>
  );
}

সমস্যা: এই CSS গ্লোবাল — যদি আরেকটা component-এও .card ক্লাস ব্যবহার করে, দুটোর স্টাইল একে অপরের সাথে সংঘর্ষ করতে পারে। Advanced CSS পর্ব ২-এ শেখা BEM এই সমস্যা কমায়, কিন্তু পুরোপুরি সমাধান করে না।

পদ্ধতি ২ — CSS Modules (সংঘর্ষ-মুক্ত)

.module.css এক্সটেনশন দিয়ে ফাইল বানালে Vite স্বয়ংক্রিয়ভাবে প্রতিটা ক্লাস নামকে "ইউনিক" বানিয়ে দেয় — এতে গ্লোবাল সংঘর্ষের ভয় থাকে না।

/* Card.module.css */
.card {
  background: white;
  border-radius: 8px;
  padding: 16px;
}

.title {
  font-size: 18px;
  font-weight: bold;
}
// Card.jsx
import styles from "./Card.module.css"; // একটা অবজেক্ট হিসেবে ইমপোর্ট হয়

function Card({ title }) {
  return (
    <div className={styles.card}>
      <h3 className={styles.title}>{title}</h3>
    </div>
  );
}

ব্রাউজারে গিয়ে দেখলে styles.card-এর আসল মান দেখাবে কিছুটা এরকম — "Card_card__a1B2c" — Vite স্বয়ংক্রিয়ভাবে ফাইলের নাম আর একটা র‍্যান্ডম হ্যাশ জুড়ে দেয়, যাতে অন্য কোনো .card ক্লাসের সাথে কখনো সংঘর্ষ না হয়।

পদ্ধতি ৩ — Inline Style রিক্যাপ

পর্ব ২-এ শেখা inline style এখনো প্রাসঙ্গিক — বিশেষ করে যখন স্টাইলের মান সম্পূর্ণ ডাইনামিক (JavaScript ভ্যারিয়েবল থেকে সরাসরি আসছে, যেমন প্রোগ্রেস বার-এর প্রস্থ)।

function ProgressBar({ percent }) {
  return (
    <div style={{ width: "100%", background: "#e5e7eb", borderRadius: "999px" }}>
      <div style={{ width: `${percent}%`, background: "#2563eb", height: "8px", borderRadius: "999px" }} />
    </div>
  );
}

এখানে width: `${percent}%` সরাসরি JavaScript থেকে গণনা করা — এটা CSS ফাইলে সহজে করা যেত না, কারণ percent রানটাইমে পাল্টায়।

কখন কোন পদ্ধতি — একনজরে

পরিস্থিতি সুপারিশ
স্থির, পুনর্ব্যবহারযোগ্য স্টাইল CSS Modules
গ্লোবাল বেস স্টাইল (reset, টাইপোগ্রাফি) সাধারণ CSS ফাইল
সম্পূর্ণ ডাইনামিক মান (JS থেকে গণনা করা) Inline Style

Conditional className — শর্তসাপেক্ষ ক্লাস

পর্ব ৭-এর Conditional Rendering প্রায়ই ক্লাস নামেও প্রয়োগ হয় — একটা state অনুযায়ী ভিন্ন ক্লাস বসানো।

function Button({ isActive }) {
  return (
    <button className={isActive ? "btn btn--active" : "btn"}>
      চাপো
    </button>
  );
}

একাধিক শর্তসাপেক্ষ ক্লাস একসাথে জুড়তে হলে template literal সবচেয়ে সহজ পদ্ধতি — Advanced JS-এ শেখা template literal সিনট্যাক্স:

function Button({ isActive, isDisabled }) {
  return (
    <button
      className={`btn ${isActive ? "btn--active" : ""} ${isDisabled ? "btn--disabled" : ""}`}
    >
      চাপো
    </button>
  );
}

সতর্কতা: খালি স্ট্রিং থাকলে ক্লাস নামে অতিরিক্ত স্পেস থাকবে — সাধারণত এতে সমস্যা হয় না, কিন্তু বড় প্রজেক্টে clsx বা classnames-এর মতো ছোট লাইব্রেরি এই কাজটা আরও পরিষ্কারভাবে করতে সাহায্য করে (এই সিরিজে আমরা vanilla পদ্ধতিতেই থাকব)।

লাইভ ডেমো — Conditional Style ও className

নিচের ট্যাব বাটনে চাপো — সিলেক্টেড ট্যাবের স্টাইল ডাইনামিকভাবে পাল্টাচ্ছে, inline style আর conditional logic মিলিয়ে।

একটা সম্পূর্ণ উদাহরণ — CSS Modules দিয়ে কার্ড কম্পোনেন্ট

চলো একটা সম্পূর্ণ উদাহরণ দেখি — CSS Modules, conditional className, আর props মিলিয়ে একটা বই কার্ড।

/* BookCard.module.css */
.card {
  background: white;
  border-radius: 8px;
  padding: 16px;
  border: 2px solid transparent;
  transition: border-color 0.3s;
}

.card.featured {
  border-color: #facc15;
}

.title {
  font-size: 16px;
  font-weight: bold;
  margin: 0 0 4px 0;
}

.available {
  color: #16a34a;
}

.unavailable {
  color: #9ca3af;
}
// BookCard.jsx
import styles from "./BookCard.module.css";

function BookCard({ title, isAvailable, isFeatured }) {
  return (
    <div className={`${styles.card} ${isFeatured ? styles.featured : ""}`}>
      <h3 className={styles.title}>{title}</h3>
      <p className={isAvailable ? styles.available : styles.unavailable}>
        {isAvailable ? "পাওয়া যাচ্ছে" : "স্টক নেই"}
      </p>
    </div>
  );
}

export default BookCard;

এখানে CSS Modules-এর ক্লাস নাম (styles.card, styles.featured) template literal দিয়ে conditional-ভাবে জোড়া লাগানো হয়েছে — এটাই এই পর্বের প্রায় সব কনসেপ্টের মিশ্রণ।

নিজে চেষ্টা করো

Exercise:

  1. একটা Badge.module.css বানাও, কয়েকটা রঙের ভ্যারিয়েন্ট (success, danger, warning) সহ।
  2. উপরের BookCard নিজে বানাও, নিজের প্রজেক্টে বসিয়ে টেস্ট করো।
  3. একটা প্রোগ্রেস বার বানাও যেখানে প্রস্থ সম্পূর্ণ ডাইনামিক (inline style দিয়ে), আর রঙ conditional className দিয়ে (৫০%-এর নিচে লাল, উপরে সবুজ)।
  4. একই ক্লাস নাম (যেমন .title) দুটো ভিন্ন .module.css ফাইলে ব্যবহার করে দেখো — সংঘর্ষ হয় কিনা যাচাই করো (হওয়া উচিত না)।

পরের পর্বে যা থাকছে

এতে শেষ হলো "Routing ও Styling" অংশ। পরের পর্বে আমরা শিখব useRef ও Custom Hook — DOM এলিমেন্টের সরাসরি রেফারেন্স নেওয়া, আর নিজের প্রথম Custom Hook বানানোর বেসিক।

একটি মন্তব্য পোস্ট করুন

নবীনতর পূর্বতন

যোগাযোগ ফর্ম