এই পর্বে যা শিখবেন
- transform কেন লেআউট না বদলে শুধু আঁকার সময় কাজ করে — আর তাই কেন অ্যানিমেশনের জন্য সেরা
translate,rotate,scale,skew— একক, দিক, ঋণাত্মক মান আর % এর অর্থ- ফাংশনের ক্রম,
transform-origin, আলাদা transform property আর ব্রাউজারের ভেতরের matrix - 3D:
perspective,preserve-3d,backface-visibilityদিয়ে ফ্ল্যাশ কার্ড আর কিউব বানানো - stacking context, fixed-এর ফাঁদ, ঝাপসা লেখা, hover ইফেক্ট আর reduced motion — বাস্তব কাজের সতর্কতা
১ · transform কী — আঁকার সময়ের কারসাজি, লেআউট অটুট
আগের পর্বগুলোতে আপনি বাক্সের জায়গা বদলেছেন নানা উপায়ে — margin দিয়ে, position দিয়ে (পর্ব ২৪), Flexbox আর Grid দিয়ে (পর্ব ২৫–২৮)। এসবের একটি মিল আছে: সবই layout-এর অংশ। একটি বাক্স বড় হলে বা সরলে ব্রাউজার আবার হিসাব করে পাশের আর পরের বাক্স কোথায় বসবে।
কিন্তু কখনো কখনো আমরা চাই শুধু চোখে দেখা ছবিটা বদলাক, পাতার কাঠামো নয়। পণ্য-কার্ডে মাউস রাখলে কার্ডটি একটু ওপরে ভেসে উঠুক। মেনু খোলার তীরচিহ্নটি ঘুরে যাক। ফ্ল্যাশ কার্ড উল্টে পেছনের লেখা দেখাক। এর জন্য CSS-এর transform property. এটি বাক্সের লেআউট শেষ হওয়ার পরে, আঁকার সময় বাক্সটিকে সরায়, ঘোরায়, বড়-ছোট করে বা হেলায়।
ক্লাসের দেয়ালে সারি ধরে ছাত্রদের ছবি টাঙানো আছে — প্রতিটি ছবির জন্য দেয়ালে একটি করে পেরেক। transform হলো একটি ছবিকে পেরেকে ঝুলিয়ে রেখেই একটু বাঁকিয়ে দেওয়া, বা আতশ কাচ ধরে বড় দেখানো। পেরেক সরেনি, তাই পাশের ছবিগুলোও সরেনি — বড় দেখানো ছবিটা হয়তো পাশেরটার ওপর একটু উঠে গেছে। আর width বা margin বদলানো হলো পেরেক উপড়ে নতুন জায়গায় বসানো — তখন পাশের সব ছবিকে নতুন করে জায়গা দিতে হয়।
নিচের এডিটরে দুটি সারি। ওপরের সারিতে লাল বাক্সটি transform: scale(1.4) দিয়ে বড় করা; নিচের সারিতে একই বাক্স সত্যিই width আর height বাড়িয়ে বড় করা। দুই সারির পার্থক্য খেয়াল করুন:
p { font-family: sans-serif; margin: 8px 0 4px; }
.row {
display: flex; gap: 10px; align-items: flex-start;
padding: 16px; background: #f1f5f9;
}
.box {
width: 70px; height: 70px;
display: grid; place-items: center;
font: 700 22px sans-serif;
background: #93c5fd; border: 2px solid #1d4ed8;
}
.a .hot { transform: scale(1.4); background: #fca5a5; }
.b .hot { width: 98px; height: 98px; background: #fca5a5; }ওপরের সারিতে লাল “২” বড় হয়ে “১” আর “৩”-এর ওপর আংশিক উঠে গেছে, কিন্তু “১” আর “৩” একচুলও সরেনি, সারির উচ্চতাও বাড়েনি — লেআউটের হিসাবে বাক্সটি এখনো ৭০px. নিচের সারিতে বাক্স সত্যিই বড় হয়েছে, তাই “৩” ডানে সরে গেছে আর পুরো সারি লম্বা হয়েছে। চেষ্টা করুন: .a .hot-এ scale(1.4)-এর বদলে translateY(30px) লিখুন — বাক্স নিচে নামবে, কিন্তু ধূসর সারি বড় হবে না, বাক্স সারির বাইরে বেরিয়ে যাবে।
কেন এমন হয়? ব্রাউজার একটি পাতা দেখানোর জন্য কয়েকটি ধাপ পার হয় (পর্ব ১-এ এর পরিচয় পেয়েছেন)। transform কোন ধাপে কাজ করে, সেটিই এর সব আচরণের চাবি। নিচের ডায়াগ্রামে ধাপে ধাপে দেখুন:
- Style
scale(1.4)মান ঠিক করা - Layout
70 × 70transform উপেক্ষিত - Paint
পিক্সেলআঁকার নির্দেশ - Composite
GPUস্তর জোড়া
- Style — মান ঠিক করা। ব্রাউজার সব CSS rule মিলিয়ে ঠিক করে লাল বাক্সের
transformহলোscale(1.4). এই ধাপে এটি শুধু একটি মান; এখনো কিছু আঁকা হয়নি। - Layout — transform কে উপেক্ষা। এখানে প্রতিটি বাক্সের মাপ আর জায়গা হিসাব হয়।
width,margin,topএখানে কাজ করে, কিন্তু transform-কে এই ধাপ গোনেই না। লেআউটের চোখে লাল বাক্স এখনো 70×70 — তাই “৩” তার জায়গাতেই থাকে। - Paint — আসল রূপে আঁকা। লেখা, রং, border, ছায়া — কোন পিক্সেল কী রঙের হবে, তার নির্দেশ তৈরি হয়। বাক্সের নিজের ছবিটা আঁকা হয় তার আসল, transform ছাড়া রূপে।
- Composite — ছবিটাকে বসানো। শেষ ধাপে আঁকা অংশগুলো জোড়া লাগে (compositing). transform সাধারণত এখানেই প্রয়োগ হয়: আঁকা ছবিটি বড় করে, ঘুরিয়ে বা সরিয়ে বসানো। অ্যানিমেশনের সময় ব্রাউজার প্রায়ই বাক্সটিকে আলাদা একটি স্তরে (layer) তুলে GPU দিয়ে শুধু সেই স্তরটি নাড়ায় — লেআউট আর paint আবার করতে হয় না। এজন্যই
leftনাড়ানোর চেয়েtranslateনাড়ানো অনেক মসৃণ।
transform বাক্সের দেখা রূপ বদলায়, লেআউটের জায়গা নয়। ফলে (১) পাশের element নড়ে না, (২) বদলানো বাক্স অন্যের ওপর উঠে যেতে পারে, (৩) অ্যানিমেশনে খরচ কম। তবে একটি জিনিস বদলায়: পাতার স্ক্রল করা যায় এমন এলাকা। একটি বাক্সকে translateX(100vw) দিয়ে ডানে ঠেলে দিলে পাতায় পাশাপাশি স্ক্রলবার এসে যেতে পারে, কারণ বাক্সটি এখনো পাতার “ভেতরে” আঁকা হচ্ছে।
২ · চার মূল ফাংশন — translate, rotate, scale, skew
transform-এর মান হলো এক বা একাধিক transform function, ফাঁকা জায়গা দিয়ে আলাদা করা: transform: translate(20px, 10px) rotate(15deg);. প্রতিটি ফাংশন এক রকম বদল। চারটি দিয়েই বেশিরভাগ কাজ হয়ে যায়:
↔ translate() — সরানো
translate(x, y), অথবা এক দিকে translateX(), translateY(). ধনাত্মক x ডানে, ধনাত্মক y নিচে (পর্দায় y নিচের দিকে বাড়ে)। % দিলে মাপা হয় element-এর নিজের মাপ থেকে।
⟳ rotate() — ঘোরানো
কোণের একক: deg (ডিগ্রি), turn (পূর্ণ পাক — 1turn = 360deg), rad. ধনাত্মক মানে ঘড়ির কাঁটার দিকে, ঋণাত্মক মানে উল্টো দিকে।
⤢ scale() — বড়-ছোট
একক ছাড়া গুণক: scale(2) দ্বিগুণ, scale(0.5) অর্ধেক, scale(1) কিছুই না। scaleX(), scaleY() এক দিকে। ঋণাত্মক মানে উল্টে যায় — scaleX(-1) আয়নার ছবি।
▱ skew() — হেলানো
skewX(20deg) আয়তক্ষেত্রকে সামান্তরিক বানায় — যেন বইয়ের তাক এক পাশে হেলে পড়েছে। skew(x, y) দুই দিকে। কম ব্যবহার হয়, মূলত সাজসজ্জায়।
পাঁচটি বাক্স নিচে। প্রতিটির চারপাশের ধূসর ডটেড দাগ হলো বাক্সের লেআউটের আসল জায়গা; রঙিন বাক্সটি transform-এর পরে যেভাবে আঁকা হলো:
.slot {
display: inline-block;
width: 90px; height: 90px; margin: 26px;
outline: 2px dashed #94a3b8;
}
.box {
height: 100%;
display: grid; place-items: center;
font: 600 15px sans-serif;
background: #a7f3d0; border: 2px solid #047857;
box-sizing: border-box;
}
.t .box { transform: translate(20px, 15px); }
.r .box { transform: rotate(30deg); }
.s .box { transform: scale(0.7); }
.k .box { transform: skewX(-20deg); }
.m .box { transform: scaleX(-1); }“সরানো” ডানে ২০px আর নিচে ১৫px সরেছে; “ঘোরানো” ঘড়ির কাঁটার দিকে ৩০° ঘুরেছে; “ছোট” মাঝখান ঘিরে ৭০% হয়েছে; “হেলানো” সামান্তরিক হয়েছে; আর “আয়না →” লেখাটি উল্টে গেছে — তীর এখন বাঁ দিকে। সবগুলো ঘটেছে বাক্সের মাঝখান ঘিরে, কারণ ডিফল্ট কেন্দ্রবিন্দু মাঝখান (অংশ ৪)। চেষ্টা করুন: rotate(30deg)-কে rotate(-0.125turn) করুন — উল্টো দিকে ৪৫°. তারপর translate(20px, 15px)-কে translate(50%) লিখুন — বাক্স ঠিক নিজের অর্ধেক চওড়া, অর্থাৎ ৪৫px ডানে সরবে।
translate-এর % — নিজের মাপ থেকে মাপা
CSS-এ সাধারণত % মাপা হয় parent থেকে — width: 50% মানে parent-এর অর্ধেক। কিন্তু translate-এর % মাপা হয় element-এর নিজের বাক্স থেকে: translateX(100%) মানে “নিজের পুরো চওড়ার সমান ডানে সরো”। পর্ব ২৪-এ মাঝখানে বসানোর যে কৌশল দেখেছেন, সেটি এই নিয়মের ওপরই দাঁড়িয়ে:
.modal {
position: absolute;
top: 50%; left: 50%; /* parent থেকে মাপা: ওপর-বাঁ কোণ মাঝখানে */
transform: translate(-50%, -50%); /* নিজের মাপ থেকে মাপা: নিজের অর্ধেক পিছিয়ে */
}এই কৌশলে মোডালের মাপ জানা না থাকলেও চলে। আজকের দিনে শুধু মাঝখানে বসানোর জন্য Grid-এর place-items: center (পর্ব ২৬) বা <dialog> (পর্ব ১১) প্রায়ই সহজতর; তবে translate-এর কৌশলটি পুরোনো কোডে সর্বত্র পাবেন, আর এর একটি লুকানো ফাঁদ অংশ ৫-এ দেখবেন।
inline element-এ transform কাজ করে না। একটি সাধারণ <span> বা <a>-তে transform: rotate(10deg) দিলে কিছুই হবে না, কারণ স্পেসিফিকেশন অনুযায়ী লাইনের ভেতরের সাধারণ inline বাক্স “transformable” নয়। display: inline-block, block, বা flex/grid item বানালে কাজ করবে। AI-এর দেওয়া আইকন-ঘোরানো কোড না চললে প্রথমে এটিই দেখুন।
৩ · ক্রম বদলালে ফল বদলায় — বাঁ থেকে ডানে পড়ুন
একাধিক ফাংশন একসাথে লিখলে তাদের ক্রম গুরুত্বপূর্ণ। গণিতে ৩ + ৫ আর ৫ + ৩ একই, কিন্তু “ঘোরো, তারপর হাঁটো” আর “হাঁটো, তারপর ঘোরো” এক নয়।
একজন রিকশাওয়ালাকে দুইভাবে বলা যায়। প্রথমটি: “সোজা ১৫০ গজ যান, তারপর ডানদিকে আধা মোড় ঘুরে দাঁড়ান।” রিকশা সোজা রাস্তায় এগিয়ে গিয়ে সেখানে মুখ ঘোরাবে। দ্বিতীয়টি: “আগে ডানদিকে আধা মোড় ঘুরুন, তারপর সামনে ১৫০ গজ যান।” এবার রিকশা ঘোরা মুখের দিকে — আড়াআড়ি — এগোবে, পৌঁছাবে সম্পূর্ণ অন্য জায়গায়। CSS-এর transform ঠিক এভাবে কাজ করে: বাঁ থেকে ডানে পড়লে প্রতিটি ফাংশন element-এর নিজের দিক বদলে দেয়, আর পরের ফাংশন সেই বদলানো দিক ধরে কাজ করে।
- শুরু। বাক্সটি নিজের জায়গায়, তার ভেতরের তীর দেখাচ্ছে বাক্সের নিজের “সামনে” — অর্থাৎ নিজের x-অক্ষ, ডান দিক। ডটেড দাগ হলো লেআউটের আসল জায়গা, যা পুরো ডায়াগ্রামে একই থাকবে।
translate(150px)— আগে সরানো। বাক্স তার সামনের দিকে, অর্থাৎ ডানে, ১৫০px সরে গেল। এখনো ঘোরেনি, তীর ডান দিকেই।- তারপর
rotate(45deg). বাক্স নতুন জায়গাতেই দাঁড়িয়ে ঘড়ির কাঁটার দিকে ৪৫° ঘুরল। রিকশা সোজা রাস্তা ধরে গিয়ে সেখানে মুখ ফিরিয়েছে। ফল: ডানে সরা, তির্যক মুখ। - এবার উল্টো ক্রম — আগে
rotate(45deg). লাল বাক্সটি আসল জায়গাতেই ৪৫° ঘুরল। খেয়াল করুন তীরটি — বাক্সের “সামনে” এখন আর ডান নয়, ডান-নিচের তির্যক দিক। বাক্সের নিজের অক্ষগুলোই ঘুরে গেছে। - তারপর
translate(150px). “সামনে ১৫০px” এখন মানে তীরের দিকে — তাই লাল বাক্স তির্যকভাবে ডান-নিচে নেমে গেল। হালকা নীল ছায়াটি ধাপ ৩-এর ফল; একই দুটি ফাংশন, শুধু ক্রম ভিন্ন, অথচ জায়গা সম্পূর্ণ আলাদা। নিয়ম: বাঁ থেকে ডানে পড়ুন, প্রতিটি ফাংশন আগেরটির বদলে দেওয়া দিক ধরে কাজ করে।
নিজে চালিয়ে মিলিয়ে দেখুন। দুটি বাক্স একই জায়গা থেকে শুরু, একই দুটি ফাংশন পেয়েছে — শুধু ক্রম আলাদা:
.stage {
position: relative; height: 240px;
background: #f8fafc; border: 1px solid #cbd5e1;
}
.ghost, .box {
position: absolute; top: 30px; left: 30px;
width: 60px; height: 60px;
}
.ghost { outline: 2px dashed #94a3b8; }
.box {
display: grid; place-items: center;
font: 700 15px sans-serif; opacity: 0.9;
}
.one { background: #93c5fd; transform: translate(150px) rotate(45deg); }
.two { background: #fca5a5; transform: rotate(45deg) translate(150px); }নীল “১” ডানে সরে গিয়ে ঘুরেছে; লাল “২” ঘুরে গিয়ে তার তির্যক “সামনে” এগিয়ে ডান-নিচে চলে গেছে। চেষ্টা করুন: .two-তে rotate(45deg)-কে rotate(90deg) করুন — এবার “২” সোজা নিচে নামবে, কারণ ঘোরার পর তার “সামনে” মানে নিচ। তারপর .one-এ লিখুন scale(2) translate(50px) আর translate(50px) scale(2) — প্রথমটিতে বাক্স ১০০px সরবে, কারণ ততক্ষণে তার মাপকাঠিই দ্বিগুণ হয়ে গেছে।
গণিতে অভ্যস্ত হলে উল্টো দিক থেকেও ভাবতে পারেন: ডান থেকে বাঁয়ে পড়ুন, আর প্রতিটি ফাংশনকে স্থির পাতার অক্ষে প্রয়োগ করুন। rotate(45deg) translate(150px) মানে আগে ১৫০px ডানে সরাও, তারপর পুরো জিনিসটা মূল কেন্দ্র ঘিরে ৪৫° ঘোরাও — ফল একই। দুটি দৃষ্টিভঙ্গিই ঠিক; নতুনদের জন্য সাধারণত “বাঁ থেকে ডানে, নিজের দিক ধরে” ভাবা সহজ।
৪ · transform-origin — কোন বিন্দু ধরে ঘুরবে
দরজা ঘোরে কবজা ধরে, ঘড়ির কাঁটা ঘোরে একেবারে গোড়া ধরে, আর পাখা ঘোরে মাঝখান ধরে। CSS-এ এই কেন্দ্রবিন্দুটি ঠিক করে transform-origin. ডিফল্ট মান 50% 50% — অর্থাৎ বাক্সের ঠিক মাঝখান। rotate, scale আর skew এই বিন্দু ঘিরে ঘটে; translate-এর ওপর এর প্রভাব নেই (সরানো তো সরানোই)।
মান লেখা যায় keyword-এ (left, right, top, bottom, center), %-এ, বা দৈর্ঘ্যে: transform-origin: left top, transform-origin: 0 100%, transform-origin: 20px 10px. প্রথম মান আড়াআড়ি (x), দ্বিতীয়টি খাড়া (y). 3D-র জন্য তৃতীয় একটি দৈর্ঘ্যও দেওয়া যায় (z).
.pane {
position: relative; display: inline-block;
width: 160px; height: 200px; margin: 6px;
background: #f1f5f9; font: 13px sans-serif;
}
.pane small { position: absolute; top: 4px; right: 6px; }
.ghost, .bar {
position: absolute; left: 20px; top: 95px;
width: 120px; height: 24px;
}
.ghost { outline: 2px dashed #94a3b8; }
.bar { background: #f59e0b; transform: rotate(40deg); }
.p1 .bar { transform-origin: center; }
.p2 .bar { transform-origin: left center; }
.p3 .bar { transform-origin: right center; }তিনটি কমলা দণ্ডই rotate(40deg) পেয়েছে, কিন্তু ঘুরেছে তিন জায়গা ধরে। প্রথমটি পাখার মতো মাঝখান ঘিরে; দ্বিতীয়টি দরজার মতো বাঁ কিনারা ধরে, ডান মাথা নিচে নেমেছে; তৃতীয়টি ডান কিনারা স্থির রেখে ঘুরেছে, তাই বাঁ মাথাটা ওপরে উঠে গেছে। চেষ্টা করুন: .bar-এর rotate(40deg)-কে scale(0.5) করুন — প্রতিটি দণ্ড নিজের origin-এর দিকে গুটিয়ে যাবে। তারপর .p2-তে লিখুন transform-origin: -40px center — কেন্দ্র বাক্সের বাইরে থাকলেও চলে।
বাস্তবে transform-origin খুব কাজে লাগে: ড্রপডাউন মেনু ওপর থেকে নিচে “খুলে” আনতে (transform-origin: top + scaleY), ঘড়ির কাঁটা বানাতে (bottom center), বা টুলটিপ তার তীরের দিক থেকে বড় হতে। একটি সাবধানতা: SVG-র ভেতরের আকারে (পর্ব ৪৪) origin-এর হিসাব ডিফল্টভাবে আলাদা, সেখানে transform-box: fill-box দিতে হয়।
৫ · আলাদা property — translate, rotate, scale আর ভেতরের matrix
transform একটি মাত্র property, যার মানে পুরো ফাংশনের তালিকা। এর একটি পুরোনো যন্ত্রণা আছে। ধরুন মোডালটি মাঝখানে বসাতে লিখেছেন transform: translate(-50%, -50%). এখন hover-এ একটু বড় করতে লিখলেন:
.modal { transform: translate(-50%, -50%); }
.modal:hover { transform: scale(1.05); } /* ✖ পুরো তালিকা বদলে গেল — translate হারাল! */
.modal:hover { transform: translate(-50%, -50%) scale(1.05); } /* পুরোনো সমাধান: সব আবার লেখো */cascade-এ (পর্ব ১৮) জেতা rule পুরো transform মানটাই বদলে দেয় — তালিকার একটি ফাংশন আলাদা করে বদলানো যায় না। ফলে hover-এ মোডাল লাফিয়ে ডান-নিচে সরে যেত। সমাধান হিসেবে CSS-এ এসেছে তিনটি আলাদা transform property: translate, rotate আর scale. প্রতিটি আলাদা property, তাই একটি বদলালে বাকিগুলো অটুট থাকে:
.modal { translate: -50% -50%; } /* কমা নেই, ফাংশনও নেই */
.modal:hover { scale: 1.05; } /* translate অক্ষত ✔ */
.icon { rotate: 45deg; } /* 3D: rotate: y 45deg; অথবা rotate: 1 1 0 30deg; */
.banner { scale: 1.2 0.8; } /* x আর y আলাদা */এই তিনটি property এখন Baseline Widely available — সব আধুনিক ব্রাউজারে কয়েক বছর ধরে চলে। খুব পুরোনো ব্রাউজার এগুলো না বুঝলে লাইনটি শুধু উপেক্ষা করবে; সেখানে ছবি ঠিক রাখা জরুরি হলে transform দিয়ে লিখুন। কিন্তু একটি প্রশ্ন থাকে: translate, rotate, scale আর transform — চারটিই একসাথে দিলে কোনটা আগে? উত্তর: আপনি যে ক্রমেই লিখুন, ব্রাউজার একটি নির্দিষ্ট ক্রমে জোড়ে।
1.card {2 scale: 1.5;3 transform: skewX(-15deg);4 rotate: 20deg;5 translate: 80px 0;6}
| এখন যোগ হলো | |
| ক্রম নম্বর |
- লেখার ক্রম এলোমেলো। rule-এ আগে
scale, তারপরtransform, শেষেtranslate. সাধারণ property-তে লেখার ক্রমের কোনো অর্থ নেই — ব্রাউজার আগে সবগুলোর মান ঠিক করে, তারপর একটি নির্দিষ্ট নিয়মে জোড়ে। - প্রথমে
translate. স্পেসিফিকেশনের নিয়মে সবার আগে আসে translate. অর্থাৎ বাক্স আগে ৮০px ডানে সরবে — তারপর বাকি সব কিছু ঘটবে সেই নতুন জায়গায়। - তারপর
rotate. ২০° ঘোরানো যোগ হলো। যেহেতু translate আগে, ঘোরানো সরে যাওয়ার দিক বদলাতে পারে না — বাক্স সবসময় সোজা ডানে সরে, তারপর ঘোরে। hover-এ এটিই সাধারণত আপনি চান। - তারপর
scale. দেড় গুণ বড় হওয়া যোগ হলো। আবারও, scale সরে যাওয়ার দূরত্বকে বড় করে না — কারণ translate আগেই হয়ে গেছে। - সবশেষে
transform-এর পুরো তালিকা।skewX(-15deg)যোগ হলো সবার শেষে। (মাঝখানেoffset-pathদিয়ে পথ ধরে চলার অংশ থাকলে, সেটি বসে scale-এর পরে, transform-এর আগে।) পুরো রূপ ঘটেtransform-originঘিরে। মনে রাখার ছন্দ: সরো → ঘোরো → বড় হও → বাকিটা। অন্য কোনো ক্রম লাগলে সব কিছুtransform-এর ভেতরেই লিখুন।
matrix() — ব্রাউজার আসলে যা হিসাব করে
আপনি যত ফাংশনই লিখুন, ব্রাউজার শেষে সবগুলোকে গুণ করে একটি মাত্র transformation matrix বানায় — ছয়টি সংখ্যার একটি ছক। 2D-তে সেটি লেখা হয় matrix(a, b, c, d, e, f): a, b, c, d ঘোরানো, বড়-ছোট আর হেলানো একসাথে বহন করে, আর e, f হলো সরানো। JavaScript-এ getComputedStyle() দিয়ে transform পড়লে আপনি ঠিক এই matrix-টিই পান। নিচের ছোট টুলটি চালিয়ে দেখুন:
(function m() {
if (!document.body.offsetWidth) return setTimeout(m, 50);
var el = document.getElementById('c');
var cs = getComputedStyle(el);
console.log('লেখা ছিল: translateX(30px) rotate(30deg)');
console.log('ব্রাউজার দিল: ' + cs.transform);
console.log('offsetWidth (লেআউটের মাপ): ' + el.offsetWidth);
var w = el.getBoundingClientRect().width;
console.log('getBoundingClientRect (আঁকা মাপ): ' + w.toFixed(1));
})();আউটপুটে দেখবেন matrix(0.866025, 0.5, -0.5, 0.866025, 30, 0): প্রথম চারটি সংখ্যা হলো cos 30° ≈ 0.866 আর sin 30° = 0.5 — ঘোরানো; শেষ দুটি 30, 0 — সরানো। আর offsetWidth এখনো 100 (লেআউট transform জানে না), কিন্তু getBoundingClientRect() দিচ্ছে প্রায় 111.6 — ঘোরানো বাক্সকে ঘিরে থাকা সোজা আয়তক্ষেত্রের চওড়া। চেষ্টা করুন: style-এ rotate(30deg)-কে rotate(90deg) করুন — matrix হবে matrix(0, 1, -1, 0, 30, 0) (খুব ছোট দশমিক ভাঙাচোরা এলে সেটি গণনার সামান্য ত্রুটি)।
আরও গভীরে: একটি 2D matrix আসলে একটি ৩×৩ ছক, যার শেষ সারি সবসময় 0 0 1 — তাই CSS শুধু বাকি ছয়টি সংখ্যা লেখে। এই কৌশলের নাম homogeneous coordinates; এতে সরানোকেও গুণ হিসেবে লেখা যায়, ফলে যেকোনো সংখ্যক ফাংশন গুণ করে একটি ছকে আনা সম্ভব — গেম ইঞ্জিন আর GPU-ও ঠিক এভাবেই কাজ করে। 3D-তে ছকটি ৪×৪, CSS-এ matrix3d() নামে ১৬টি সংখ্যা। Matrix গুণ commutative নয় (A×B ≠ B×A), আর অংশ ৩-এর “ক্রম বদলালে ফল বদলায়” আসলে এই গাণিতিক সত্যেরই চেহারা। transition-এর সময় দুই পাশের ফাংশন-তালিকা না মিললে ব্রাউজার matrix-কে আবার translate, rotate, scale, skew-এ ভেঙে (decompose) মাঝের ধাপগুলো হিসাব করে।
৬ · 3D-র জগৎ — z-অক্ষ, perspective আর দূরত্বের বিভ্রম
পর্দা সমতল, কিন্তু CSS-এ একটি তৃতীয় অক্ষ আছে — z-অক্ষ, যা পর্দা ভেদ করে আপনার চোখের দিকে আসে। ধনাত্মক z মানে আপনার দিকে, ঋণাত্মক মানে পর্দার ভেতরে। এই অক্ষ ধরে কাজ করে নতুন কয়েকটি ফাংশন:
| ফাংশন | কী করে | বাস্তবের তুলনা |
|---|---|---|
rotateX(a) | আড়াআড়ি অক্ষ ঘিরে ঘোরে — ওপর-নিচে উল্টায় | ক্যালেন্ডারের পাতা ওল্টানো |
rotateY(a) | খাড়া অক্ষ ঘিরে ঘোরে — বাঁ-ডানে উল্টায় | দরজা খোলা, বইয়ের পাতা ওল্টানো |
rotateZ(a) | পর্দার দিকের অক্ষ ঘিরে ঘোরে — সাধারণ rotate()-এর সমান | ঘড়ির কাঁটা |
translateZ(z) | চোখের দিকে আনে বা দূরে পাঠায় | ছবি হাতে নিয়ে চোখের কাছে আনা |
translate3d(), scale3d(), rotate3d() | তিন অক্ষ একসাথে | — |
কিন্তু শুধু rotateY(50deg) দিলে মজার কিছু দেখবেন না — কার্ডটি শুধু একটু সরু দেখাবে। কারণ গভীরতার বিভ্রম তৈরি করতে চাই perspective — দর্শক পর্দা থেকে কত দূরে বসে আছেন। কাছের জিনিস বড়, দূরের জিনিস ছোট দেখায়; perspective ছাড়া ব্রাউজার সব কিছু সমান মাপে চ্যাপ্টা করে আঁকে। নিচের ডায়াগ্রামে একই কার্ড, একই rotateY(50deg) — শুধু perspective বদলাচ্ছে:
- perspective নেই। কার্ডটি খাড়া অক্ষ ঘিরে ৫০° ঘুরেছে, কিন্তু দেখাচ্ছে শুধু একটি সরু আয়তক্ষেত্রের মতো — দুই কিনারা সমান উঁচু। ব্রাউজার “অসীম দূর” থেকে দেখার মতো চ্যাপ্টা করে এঁকেছে; গভীরতার কোনো অনুভূতি নেই।
perspective: 1200px— দূর থেকে দেখা। parent-এ দর্শকের দূরত্ব ১২০০px বসানো হলো। এবার কার্ডের দূরের কিনারা সামান্য ছোট — হালকা গভীরতা। বড় মান মানে দর্শক দূরে, বিকৃতি কম।perspective: 400px— স্বাভাবিক গভীরতা। দূরের কিনারা স্পষ্টভাবে ছোট, কাছের কিনারা বড় — সত্যিকারের দরজা খোলার মতো। কার্ড ফ্লিপ বা কিউবে সাধারণত ৫০০–১০০০px-এর কাছাকাছি মান ভালো দেখায়।perspective: 160px— একেবারে চোখের সামনে। দর্শক খুব কাছে, তাই বিকৃতি প্রবল — কাছের কিনারা অনেক বড়, দূরেরটা অনেক ছোট। মোবাইল ক্যামেরা মুখের খুব কাছে ধরলে নাক যেমন বড় দেখায়। খুব ছোট মান সাধারণত অস্বাভাবিক লাগে।perspective-origin— দর্শক কোথায় দাঁড়িয়ে। ডিফল্টে দর্শক ঠিক মাঝখান থেকে দেখেন।perspective-origin: leftদিলে মনে হয় বাঁ দিক থেকে দেখছেন, তাই একই ঘোরানো কার্ড অন্যরকম দেখায়। মানটি parent-এ বসে, কারণ এটি পুরো “মঞ্চের” দর্শকের অবস্থান।
perspective property বনাম perspective() ফাংশন
perspective দেওয়ার দুটি উপায় আছে, আর পার্থক্যটা গুরুত্বপূর্ণ। perspective property বসে parent-এ, আর তার সব সন্তান একটি সাধারণ দর্শক আর একটি vanishing point ভাগ করে নেয় — যেন সবাই একই মঞ্চে। perspective() ফাংশন বসে element-এর নিজের transform-এ, আর প্রতিটি element নিজের আলাদা দর্শক পায়। ফাংশনটি তালিকার প্রথমে লিখতে হয়, না হলে আগের ফাংশনগুলোর ওপর এর প্রভাব পড়ে না।
p { font-family: sans-serif; margin: 6px 0; }
.row {
display: flex; gap: 18px; justify-content: center;
padding: 18px; background: #f1f5f9;
}
.card {
width: 90px; height: 110px; border-radius: 8px;
display: grid; place-items: center;
font: 700 22px sans-serif; color: #fff;
background: linear-gradient(90deg, #1d4ed8, #60a5fa);
}
.scene { perspective: 400px; }
.scene .card { transform: rotateY(45deg); }
.solo .card { transform: perspective(400px) rotateY(45deg); }ওপরের সারিতে তিনটি কার্ড একই মঞ্চে — দর্শক সারির মাঝখানে, তাই বাঁয়ের কার্ড, মাঝের কার্ড আর ডানের কার্ড একটু আলাদা কোণে দেখা যাচ্ছে, যেন জানালার সারি। নিচের সারিতে প্রতিটি কার্ডের নিজের দর্শক, তাই তিনটিই হুবহু একই রকম। চেষ্টা করুন: .solo .card-এ ক্রম উল্টে লিখুন rotateY(45deg) perspective(400px) — গভীরতা হারিয়ে চ্যাপ্টা হয়ে যাবে। তারপর .scene-এ perspective-origin: left যোগ করুন।
৭ · preserve-3d আর backface-visibility — ফ্ল্যাশ কার্ড আর কিউব
3D-র আরও দুটি property ছাড়া কার্ড ফ্লিপ বা কিউব বানানো যায় না।
transform-style: preserve-3d — ডিফল্টে (flat) একটি element-এর সন্তানরা যত 3D-ই হোক, parent তাদের নিজের সমতলে চ্যাপ্টা করে আঁকে — যেন একটি ছবির ফ্রেমের ভেতরে। preserve-3d দিলে সন্তানরা একই 3D জগতে নিজের গভীরতা নিয়ে থাকে। কিউবের ছয়টি মুখ ঘোরাতে হলে মোড়কে এটি লাগবেই।
backface-visibility: hidden — প্রতিটি element-এর একটি পেছনের দিক আছে। ডিফল্টে ১৮০° ঘোরালে পেছন থেকে লেখাটি আয়নার মতো উল্টো দেখা যায়। hidden দিলে পেছন ফিরলেই element অদৃশ্য — ফ্ল্যাশ কার্ডের সামনের দিক উল্টালে লুকিয়ে যায়, আর পেছনের দিকটা দেখা দেয়। আজ এটি prefix ছাড়াই সব আধুনিক ব্রাউজারে চলে (Baseline Widely available); একসময় Safari-তে -webkit- লাগত, নতুন কোডে আর দরকার নেই।
এবার একটি ইংরেজি শব্দ শেখার ফ্ল্যাশ কার্ড বানাই — সামনে বাংলা শব্দ, পেছনে ইংরেজি অর্থ। কার্ডটি একটি আসল <button>, তাই মাউস, কীবোর্ড (Tab, তারপর Enter বা Space) আর টাচ — সবভাবেই উল্টানো যায়:
<style>
.card { border: 0; padding: 0; background: none; cursor: pointer;
width: 240px; height: 140px; margin: 20px; perspective: 800px; }
.inner { display: block; position: relative; height: 100%;
transform-style: preserve-3d; transition: transform 0.6s; }
.card[aria-pressed="true"] .inner { transform: rotateY(180deg); }
.face { position: absolute; inset: 0; display: grid; place-items: center;
border-radius: 14px; font: 700 30px sans-serif;
backface-visibility: hidden; }
.front { background: #fde68a; color: #78350f; }
.back { background: #1e3a8a; color: #fff; transform: rotateY(180deg); }
@media (prefers-reduced-motion: reduce) { .inner { transition: none; } }
</style>
<button class="card" aria-pressed="false">
<span class="inner">
<span class="face front">নদী</span>
<span class="face back" lang="en">river</span>
</span>
</button>
<script>
document.querySelector('.card').addEventListener('click', function () {
this.setAttribute('aria-pressed', this.getAttribute('aria-pressed') !== 'true');
});
</script>হলুদ কার্ডে “নদী” লেখা। ক্লিক করুন (বা Tab দিয়ে এসে Space চাপুন) — কার্ড খাড়া অক্ষ ঘিরে উল্টে নীল দিকে “river” দেখাবে; আবার চাপলে ফিরে আসবে। চারটি অংশ মিলে কাজটি হচ্ছে: বোতামে perspective, .inner-এ preserve-3d, দুই মুখে backface-visibility: hidden, আর পেছনের মুখ আগে থেকেই rotateY(180deg) করে রাখা। চেষ্টা করুন: .face থেকে backface-visibility: hidden; মুছুন — উল্টানোর পর “নদী” আয়নার মতো উল্টো দেখা যাবে। তারপর .inner-এর preserve-3d মুছুন — দুই মুখ একই সমতলে চ্যাপ্টা হয়ে যাবে, আর পেছনের দিক আর ঠিকমতো আসবে না।
কেন <div>-এ :hover দিয়ে নয়? কারণ hover শুধু মাউসে হয় — ফোনে আর কীবোর্ডে কার্ড উল্টাবেই না। <button> নিজে থেকেই focus পায় আর Enter/Space বোঝে, আর aria-pressed screen reader-কে জানায় কার্ডটি এখন উল্টানো কি না (পর্ব ১২)। দুই মুখের লেখাই DOM-এ আছে, তাই screen reader বোতামের নাম হিসেবে “নদী river” পড়বে — শব্দ শেখার কার্ডে সেটি ক্ষতিকর নয়; কুইজ বানালে উত্তরের দিকটি পড়া বন্ধ রাখার ব্যবস্থা ভাবতে হবে।
একটি সহজ 3D কিউব
কিউবের কৌশল অংশ ৩-এর ক্রমের নিয়মেরই প্রয়োগ। ছয়টি মুখ শুরুতে একই জায়গায় স্তূপ করা। প্রতিটি মুখকে আগে নির্দিষ্ট দিকে ঘোরান, তারপর নিজের “সামনে” — অর্থাৎ z-অক্ষ ধরে — অর্ধেক কিউবের সমান translateZ দিয়ে ঠেলে দিন। ঘোরানোর পর প্রতিটির “সামনে” আলাদা দিকে, তাই ছয়টি মুখ ছয় দিকে সরে বাক্সের দেয়াল হয়ে দাঁড়ায়:
.scene { width: 120px; height: 120px; margin: 70px auto; perspective: 600px; }
.cube {
position: relative; width: 100%; height: 100%;
transform-style: preserve-3d;
transform: rotateX(-25deg) rotateY(35deg);
}
.face {
position: absolute; inset: 0;
display: grid; place-items: center;
font: 700 30px sans-serif; color: #fff;
border: 2px solid #fff; opacity: 0.9;
}
.f1 { background: #2563eb; transform: translateZ(60px); }
.f2 { background: #16a34a; transform: rotateY(90deg) translateZ(60px); }
.f3 { background: #dc2626; transform: rotateY(180deg) translateZ(60px); }
.f4 { background: #9333ea; transform: rotateY(-90deg) translateZ(60px); }
.f5 { background: #ea580c; transform: rotateX(90deg) translateZ(60px); }
.f6 { background: #0891b2; transform: rotateX(-90deg) translateZ(60px); }একটি কিউব দেখা যাচ্ছে — সামনে-ডানে নীল “১”, বাঁ পাশে বেগুনি “৪”, ওপরে কমলা “৫”. মুখগুলো সামান্য স্বচ্ছ, তাই পেছনের মুখের আভাসও পাবেন। গোটা কিউবটি .cube-এর rotateX(-25deg) rotateY(35deg) দিয়ে একটু হেলিয়ে রাখা, যাতে তিনটি দিক দেখা যায়। চেষ্টা করুন: rotateY(35deg)-কে rotateY(125deg) করুন — কিউব আরও ঘুরে বেগুনি “৪” আর পেছনের লাল “৩”-কে সামনে আনবে। তারপর .cube-এ overflow: hidden; যোগ করুন — কিউব ভেঙে চ্যাপ্টা হয়ে যাবে (কারণ নিচের সতর্কবার্তায়)।
preserve-3d চুপচাপ বন্ধ হয়ে যায় কিছু property থাকলে। স্পেসিফিকেশন এদের বলে “grouping property” — যেমন overflow (visible বা clip ছাড়া), opacity ১-এর কম, filter, clip-path, mask, isolation: isolate বা mix-blend-mode. এগুলো element-কে একটি সমতল ছবিতে গুটিয়ে আঁকতে বাধ্য করে, তাই preserve-3d-এর মোড়কে দিলে 3D ভেঙে যায়। ব্রাউজার কোনো error দেখায় না — DevTools-এ computed style দেখে খুঁজতে হয়। স্বচ্ছতা দরকার হলে মোড়কে নয়, মুখগুলোতে দিন (যেমন উপরের কিউবে)।
৮ · পার্শ্বপ্রতিক্রিয়া — stacking context, fixed-এর ফাঁদ আর ঝাপসা লেখা
transform শুধু ছবি বদলায় না, কয়েকটি নিয়মও চুপচাপ বদলে দেয়। এগুলো না জানলে এমন বাগ হয় যার কারণ খুঁজে পাওয়া কঠিন।
১. নতুন stacking context
none ছাড়া যেকোনো transform — এবং translate, rotate, scale-এর none ছাড়া যেকোনো মান — element-কে একটি নতুন stacking context বানায় (পর্ব ২৪)। ফলে তার ভেতরের z-index: 9999-ও বাইরের অন্য দলের ওপরে উঠতে পারে না। hover-এ কার্ড একটু বড় করার জন্য scale: 1.02 দেওয়ার পর ড্রপডাউন পাশের কার্ডের নিচে চলে গেলে এটিই কারণ। বোনাস হিসেবে transform-ওয়ালা element নিজে অন্য সাধারণ (non-positioned) ভাইবোনের ওপরে আঁকা হয়, যেন position: relative পেয়েছে।
২. fixed বাক্সের containing block
এটিই সবচেয়ে বড় ফাঁদ। সাধারণত position: fixed বাক্স viewport-এর সাথে আটকে থাকে। কিন্তু কোনো পূর্বপুরুষে transform থাকলে সেই পূর্বপুরুষই তার containing block হয়ে যায় — fixed বাক্স তখন পাতার সাথে স্ক্রল হয়ে চলে যায়:
.page { transform: translateX(0); } /* নিরীহ দেখায় — হয়তো স্লাইড অ্যানিমেশনের জন্য */
.chat-btn { position: fixed; right: 16px; bottom: 16px; }
/* ফল: চ্যাট বোতাম viewport নয়, .page-এর কোণে বসে, আর স্ক্রলে চলে যায় */একই কাজ করে perspective, filter, will-change: transform আর আলাদা transform property-গুলোও। সমাধান: fixed বা মোডাল অংশ transform-ওয়ালা মোড়কের বাইরে রাখুন — সরাসরি <body>-এর সন্তান করে, অথবা <dialog>/popover ব্যবহার করে, যারা top layer-এ ওঠে (পর্ব ১১, ২৪)।
৩. ঝাপসা লেখা আর ভগ্নাংশ পিক্সেল
কখনো দেখবেন transform দেওয়া বাক্সের লেখা একটু ঝাপসা। দুটি সাধারণ কারণ। প্রথমটি ভগ্নাংশ পিক্সেল: translate(-50%, -50%) দিয়ে মাঝখানে বসানো বাক্সের উচ্চতা বিজোড় হলে (ধরুন ২০১px) সরানোর পরিমাণ হয় ১০০.৫px; লেখা তখন দুই পিক্সেলের মাঝখানে পড়ে, আর কিছু ব্রাউজারে বা স্ক্রিনে কিনারা নরম দেখায়। দ্বিতীয়টি বড় করা ছবি: ব্রাউজার একটি স্তরকে ছোট অবস্থায় আঁকার পর GPU দিয়ে scale করে বড় করলে, অনেক সময় নতুন করে না আঁকা পর্যন্ত সেটি ঝাপসা থাকে — বিশেষ করে will-change: transform দেওয়া থাকলে। আচরণটি ব্রাউজার আর ডিভাইসভেদে আলাদা।
- শুধু মাঝখানে বসাতে transform-এর বদলে Grid/Flexbox (
place-items: center) ব্যবহার করুন — ভগ্নাংশ সরানোর প্রশ্নই আসে না। - বড় হওয়ার অ্যানিমেশনে উল্টো দিক থেকে ভাবুন: আসল (বড়) মাপে বানিয়ে শুরুতে
scale: 0.9দিন, শেষেscale: 1— শেষ অবস্থা হুবহু আসল মাপে। will-change: transformশুধু সত্যিই অ্যানিমেশন হওয়া অল্প কয়েকটি element-এ, সব জায়গায় নয়; প্রতিটি আলাদা স্তর GPU-র মেমরি খায়।
পরীক্ষা ও ইন্টারভিউতে — বিশ্ববিদ্যালয়, চাকরি বা ফ্রন্ট-এন্ড ইন্টারভিউর পরীক্ষায় এ ধরনের প্রশ্ন প্রায়ই আসে:
transform: translate()আরposition: relative+left-এর পার্থক্য? — দুটিই জায়গা রেখে সরায়, কিন্তু translate লেআউট ধাপে হিসাব হয় না, % মাপে নিজের মাপ থেকে, আর অ্যানিমেশনে অনেক সস্তা।rotate(45deg) translate(100px)আর উল্টো ক্রম কি এক? — না; বাঁ থেকে ডানে প্রতিটি ফাংশন নিজের বদলে যাওয়া অক্ষে কাজ করে (matrix গুণ commutative নয়)।perspectiveproperty আরperspective()ফাংশনের পার্থক্য? — property parent-এ বসে সব সন্তানকে একটি সাধারণ vanishing point দেয়; ফাংশন প্রতিটি element-কে নিজের আলাদা দর্শক দেয়।- কার্ড ফ্লিপে কোন তিনটি property লাগে? — parent-এ
perspective, মোড়কেtransform-style: preserve-3d, দুই মুখেbackface-visibility: hidden. - transform-ওয়ালা parent-এর ভেতরে fixed element স্ক্রল হয়ে যায় কেন? — transform fixed বংশধরের containing block বানায়, তাই সে viewport-এর বদলে parent-এর সাপেক্ষে বসে।
৯ · hover ইফেক্ট, transition আর নড়াচড়ার দায়িত্ব
transform-এর সবচেয়ে প্রচলিত ব্যবহার হলো transition-এর সাথে ছোট ইফেক্ট (পর্ব ৩৮): মাউস রাখলে কার্ড একটু ওপরে ভাসে, আইকন একটু ঘোরে। আলাদা transform property থাকায় প্রতিটি অংশকে আলাদা সময় আর গতি দেওয়া যায়। নিচের কার্ডগুলোতে মাউস রাখুন, অথবা Tab চেপে focus আনুন:
.card {
display: inline-block; width: 170px; margin: 24px 10px;
padding: 18px; border-radius: 12px;
font: 600 16px sans-serif; color: #0f172a; text-decoration: none;
background: #fff; border: 1px solid #cbd5e1;
transition: translate 0.2s ease, scale 0.2s ease, box-shadow 0.2s ease;
}
.card:hover, .card:focus-visible {
translate: 0 -6px; scale: 1.03;
box-shadow: 0 12px 20px rgb(0 0 0 / 0.15);
}
.icon { display: inline-block; transition: rotate 0.3s ease; }
.card:hover .icon, .card:focus-visible .icon { rotate: -15deg; }
@media (prefers-reduced-motion: reduce) {
.card, .icon { transition: none; }
}মাউস রাখলে কার্ড ৬px ওপরে ভেসে একটু বড় হয়, নিচে ছায়া পড়ে, আর আইকন একটু হেলে যায়। আইকনের <span>-এ display: inline-block খেয়াল করুন — ছাড়া ঘুরত না (অংশ ২). চেষ্টা করুন: translate: 0 -6px-কে translate: 0 -30px করুন — বড় লাফ কতটা বিরক্তিকর লাগে দেখুন। তারপর অপারেটিং সিস্টেমে “কম অ্যানিমেশন” চালু করে (Windows: Settings → Accessibility → Visual effects → Animation effects বন্ধ) আবার দেখুন — পরিবর্তন হবে, কিন্তু চোখের পলকে, কোনো নড়াচড়া ছাড়া।
অ্যাক্সেসিবিলিটি — নড়াচড়া সবার জন্য আরামদায়ক নয়
কিছু মানুষের ভেতরের কানের ভারসাম্য-ব্যবস্থায় (vestibular system) সমস্যা থাকে; বড় নড়াচড়া, ঘূর্ণন, zoom বা parallax দেখলে তাঁদের মাথা ঘোরে, বমি ভাব হয়, মাথাব্যথা হয়। তাই নিয়মগুলো এমন:
WCAG 2.2-এর WCAG 2.3.3 “Animation from Interactions” (AAA স্তর) বলে, ব্যবহারকারীর কাজে শুরু হওয়া অপ্রয়োজনীয় নড়াচড়া বন্ধ করার উপায় থাকা উচিত। AAA বাধ্যতামূলক না হলেও prefers-reduced-motion মানা আজ পেশাদার কাজের ন্যূনতম মান। reduced motion আর বড় লেখার বিস্তারিত পর্ব ৪৫-এ।
CSS transform শুরু হয় Apple-এর WebKit দলের হাতে — ২০০৭ সালে তারা Safari-র ইঞ্জিনে -webkit-transform নামে 2D transform যোগ করে, মূলত iPhone-এর মসৃণ ইন্টারফেসের জন্য; ২০০৯ সালের দিকে আসে 3D transform. পরে Apple transform, transition আর animation-এর প্রস্তাব W3C-তে জমা দেয়, আর অন্য ব্রাউজাররা নিজের prefix দিয়ে (-moz-, -ms-, -o-) একই জিনিস চালু করে। সেই যুগের কোডে এক লাইনের জন্য চার-পাঁচটি prefix লেখা হতো। আজ মানদণ্ডটি দুই ভাগে: CSS Transforms Module Level 1 (2D) আর Level 2 (3D এবং translate/rotate/scale property). prefix এখন শুধু ইতিহাস।
১০ · AI-এর যুগে — transform-এর কাজ AI-কে দিলে কী দেখবেন
“একটা কার্ড ফ্লিপ বানাও” বা “hover-এ কার্ড ভাসাও” বললে AI মুহূর্তেই কোড দেয়, আর সেটা চলেও। কিন্তু AI শিখেছে বহু বছরের পুরোনো টিউটোরিয়াল থেকে, তাই prefix-এ ভরা, কীবোর্ডে অচল, নড়াচড়া-সংবেদনশীল মানুষের কথা না ভাবা কোড প্রায়ই আসে।
AI সাধারণত যেসব ভুল করে
| AI-এর ভুল | আপনি যা দেখবেন | ঠিক উপায় |
|---|---|---|
-webkit-transform, -moz-transform, -webkit-backface-visibility | অপ্রয়োজনীয় বাড়তি লাইন | prefix ছাড়া লিখুন |
মাঝখানে বসানো translate(-50%, -50%), আর hover-এ শুধু transform: scale() | hover-এ বাক্স লাফিয়ে সরে যায় | আলাদা translate আর scale property |
left/top/width দিয়ে অ্যানিমেশন | ফোনে ঝাঁকুনি, পাশের লেখা নড়ে | translate/scale দিয়ে অ্যানিমেশন |
div:hover দিয়ে কার্ড ফ্লিপ | ফোনে আর কীবোর্ডে উল্টায় না | <button> + aria-pressed |
সব জায়গায় will-change: transform বা translateZ(0) “GPU hack” | মেমরি বেশি খরচ, fixed অংশ ভাঙে, লেখা ঝাপসা | শুধু দরকারি জায়গায়, মেপে দেখে |
prefers-reduced-motion নেই | নড়াচড়া-সংবেদনশীল ব্যবহারকারীর অস্বস্তি | media query দিয়ে নড়াচড়া কমান |
AI-কে কাজ দেওয়ার সময় শর্তগুলো আগেই লিখে দিন। শেষের অনুরোধটি খেয়াল করুন — প্রতিটি transform কেন দেওয়া, আর কোনটি নতুন stacking context বানাচ্ছে, তা লিখতে বললে AI-এর নিজের ভুলও প্রায়ই বেরিয়ে আসে।
AI-এর দেওয়া কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলোই আপনাকে কোড-লেখক থেকে সিস্টেম-চিন্তক বানাবে।
প্র ০১transform লেআউট বদলায় না — এটা কি দুর্বলতা, না ইচ্ছাকৃত নকশা? অ্যানিমেশনের গতির সাথে এর সম্পর্ক কী?
এটি সম্পূর্ণ ইচ্ছাকৃত, আর ওয়েবের পারফরম্যান্সের ইতিহাসে খুব গুরুত্বপূর্ণ একটি সিদ্ধান্ত। একটি পাতার লেআউট হিসাব করা ব্যয়বহুল কাজ: একটি বাক্সের মাপ বদলালে তার পরের ভাইবোন, parent, এমনকি পুরো পাতার বহু element-এর জায়গা নতুন করে গুনতে হতে পারে (reflow). ৬০ ফ্রেম প্রতি সেকেন্ডে মসৃণ অ্যানিমেশনের জন্য প্রতিটি ফ্রেমে ব্রাউজারের হাতে মোটামুটি ১৬ মিলিসেকেন্ড; সস্তা ফোনে প্রতি ফ্রেমে লেআউট আর paint করলে সেই সময় প্রায়ই ফুরিয়ে যায়, ফলে ঝাঁকুনি (jank) দেখা দেয়।
transform-কে লেআউট থেকে আলাদা রাখায় ব্রাউজার একটি কৌশল নিতে পারে: অ্যানিমেশন শুরুর আগে element-কে আলাদা একটি স্তরে একবার এঁকে রাখা, তারপর প্রতি ফ্রেমে শুধু সেই ছবিটিকে GPU দিয়ে সরানো বা ঘোরানো। আধুনিক ব্রাউজারে এই কাজ অনেক সময় একটি আলাদা compositor thread-এ চলে, তাই JavaScript-এর ভারী কাজে মূল thread ব্যস্ত থাকলেও transform আর opacity-র অ্যানিমেশন প্রায়ই মসৃণ থাকে। এজন্যই পারফরম্যান্সের পুরোনো পরামর্শ: “অ্যানিমেশন করুন শুধু transform আর opacity দিয়ে।”
দাম আছে অবশ্যই। লেআউট না বদলানোয় বড় করা element অন্যকে ঢেকে দেয়, আর আপনাকেই জায়গার ব্যবস্থা রাখতে হয়। প্রতিটি আলাদা স্তর GPU-র মেমরি খায়, তাই সব কিছু স্তরে তুললে উল্টো ফল হয়। আর “লেআউট বদলায় না” মানে “কিছুই বদলায় না” নয় — stacking context আর containing block বদলায়, যা অংশ ৮-এ দেখেছেন। এই কারণেই Core Web Vitals-এর CLS মাপকাঠিতেও transform দিয়ে সরানো জিনিসকে সাধারণত layout shift ধরা হয় না, কারণ অন্য কারও জায়গা নড়ে না।
মূল উপলব্ধি: ভালো নকশা প্রায়ই একটি কাজকে দুই ভাগে ভাগ করে — “কোথায় জায়গা” আর “কেমন দেখায়”। transform দ্বিতীয় ভাগের হাতিয়ার; তাই এটি দ্রুত, কিন্তু প্রথম ভাগের কাজ এটি দিয়ে করা উচিত নয়।
প্র ০২rotate(0deg) থেকে rotate(360deg)-এ transition দিলে কার্ড এক পাক ঘোরে, অথচ দুটির matrix হুবহু এক। ব্রাউজার তাহলে মাঝের ধাপগুলো বোঝে কীভাবে?
প্রশ্নটি transform-এর ভেতরের গণিতের একটি সূক্ষ্ম দিক ধরে। ০° আর ৩৬০° ঘোরানোর matrix একই — matrix(1, 0, 0, 1, 0, 0), অর্থাৎ কিছুই না করা। শুধু matrix দেখে মাঝের অবস্থা হিসাব করলে কার্ড একটুও নড়ত না। তাই স্পেসিফিকেশন transition আর animation-এর জন্য দুই রকম পথ রেখেছে।
পথ ১ — ফাংশন ধরে ধরে: শুরু আর শেষের ফাংশন-তালিকা যদি মিলে যায় (একই ক্রমে একই ধরনের ফাংশন — যেমন দুই পাশেই শুধু rotate()), তাহলে ব্রাউজার প্রতিটি ফাংশনের সংখ্যাটি আলাদাভাবে মাঝামাঝি করে: ০° থেকে ৩৬০°, ধাপে ধাপে। ফলে পুরো এক পাক ঘোরা দেখা যায়। একপাশে none থাকলে সেটিকে অন্য পাশের মতো “কিছু না করা” ফাংশন-তালিকা ধরে নেওয়া হয়, তাই none থেকে rotate(360deg)-ও ঘোরে।
পথ ২ — matrix ভেঙে: তালিকা না মিললে (ধরুন একপাশে scale(1) rotate(0deg), অন্য পাশে rotate(360deg) scale(1)), ব্রাউজার দুই পাশকে matrix বানায়, তারপর প্রতিটিকে translate, rotate, scale, skew-এ “ভেঙে” (decomposition) সেই অংশগুলোর মাঝামাঝি হিসাব করে। দুই পাশের matrix এখানে একই, তাই কোনো নড়াচড়া হবে না! এটি বাস্তবের একটি বিখ্যাত বিভ্রান্তি — ক্রম বদলে দিলেই অ্যানিমেশন “অদৃশ্য” হয়ে যায়।
আলাদা rotate property এখানে আরও সুবিধা দেয়: সেটি সবসময় একটি মাত্র কোণ, তাই rotate: 0deg থেকে rotate: 720deg সবসময় দুই পাক ঘোরে, তালিকা মেলানোর ঝামেলা নেই। 3D ঘোরানোর ক্ষেত্রে ব্রাউজার আরও জটিল গণিত (quaternion-এর মতো উপায়) ব্যবহার করে, যাতে মাঝের অবস্থাগুলো স্বাভাবিক দেখায় — গেম আর অ্যানিমেশন সফটওয়্যারও একই কৌশলে চলে।
মূল উপলব্ধি: চূড়ান্ত অবস্থা এক হলেও “সেখানে পৌঁছানোর পথ” আলাদা হতে পারে। অ্যানিমেশন করার সময় শুরু আর শেষের ফাংশন-তালিকা একই ক্রমে রাখুন, অথবা আলাদা property ব্যবহার করুন।
প্র ০৩আলাদা translate/rotate/scale property একটি নির্দিষ্ট ক্রমে জোড়া লাগে। লেখার ক্রম মেনে চলাই কি বেশি স্বাভাবিক হতো না?
প্রথম দেখায় মনে হয় “আমি যে ক্রমে লিখেছি, সে ক্রমেই হোক”। কিন্তু CSS-এর মূল নকশায় property-র কোনো ক্রম নেই। একটি element-এর scale একটি rule থেকে আসতে পারে, rotate অন্য stylesheet থেকে, translate একটি :hover rule থেকে, আর আরেকটি animation থেকে। cascade (পর্ব ১৮) প্রতিটি property-র বিজয়ী মান আলাদাভাবে ঠিক করে; “কোনটা আগে লেখা হয়েছিল” — এই প্রশ্নের কোনো একক উত্তর তখন থাকে না। তাই একটি স্থির ক্রম ছাড়া উপায় ছিল না।
কোন ক্রম? translate → rotate → scale বেছে নেওয়ার পেছনে বাস্তব যুক্তি আছে: মানুষ সাধারণত চায় জিনিসটা “সেখানে যাক, সেখানে ঘুরুক, সেখানে বড় হোক”। translate আগে থাকায় ঘোরানো বা বড় করা সরে যাওয়ার দিক বা দূরত্ব বদলায় না — hover-এ scale: 1.05 দিলে মোডাল তার জায়গা থেকে লাফ দেয় না। এটিই ৩D মডেলিং সফটওয়্যারেও প্রচলিত “TRS” (translate-rotate-scale) ক্রম, যা গেম ইঞ্জিনে বহু দশক ধরে চলছে।
এই সুবিধার দাম হলো নমনীয়তা। অংশ ৩-এর মতো “আগে ঘোরো, তারপর ঘোরা দিকে হাঁটো” আলাদা property দিয়ে করা যায় না। তখন transform-এর ভেতরে পুরো তালিকা লিখতে হয়, যা সবশেষে জোড়া লাগে। অর্থাৎ দুটি ব্যবস্থা একে অপরের পরিপূরক: সাধারণ কাজের জন্য আলাদা property, বিশেষ ক্রমের জন্য transform.
একটি সাধারণ ভুল ধারণা: “আলাদা property এলে transform বাতিল হয়ে যাবে।” না — transform থাকছে, এবং 3D কিউবের মতো জটিল ক্রমের কাজে সেটিই একমাত্র উপায়। ইতিহাসে এই property-গুলো ব্রাউজারে এসেছে ধীরে ধীরে: Firefox আগে, Chrome সবার শেষে ২০২২ সালে; তারপর থেকে এগুলো Baseline-এ, আর এখন Widely available.
মূল উপলব্ধি: ঘোষণামূলক ভাষায় (declarative language) “লেখার ক্রম” অর্থহীন হলে নকশাকারীদের একটি নিয়ম বেঁধে দিতে হয়। ভালো নিয়ম বেছে নেওয়া হয় সবচেয়ে প্রচলিত প্রয়োজন দেখে, আর বিশেষ প্রয়োজনের জন্য একটি পেছনের দরজা খোলা রাখা হয়।
প্র ০৪perspective-এর ধারণা কোথা থেকে এল? CSS-এর perspective: 400px আসলে কী মাপছে, আর translateZ খুব বেশি দিলে কী হয়?
সমতল পটে গভীরতার বিভ্রম আঁকার গণিতটি শিল্পের ইতিহাসের বড় আবিষ্কার। পঞ্চদশ শতকে ইতালির ফ্লোরেন্সে স্থপতি Filippo Brunelleschi রৈখিক পরিপ্রেক্ষিত (linear perspective) নিয়ে পরীক্ষা করেন, আর পরে Leon Battista Alberti তা লিখিত নিয়মে বাঁধেন। মূল কথা: সমান্তরাল রেখাগুলো দূরে গিয়ে একটি বিন্দুতে মেলে — vanishing point — আর জিনিস যত দূরে, তত ছোট। রেলপথের দুই লাইন দিগন্তে মিশে যাওয়ার দৃশ্য এর সবচেয়ে চেনা উদাহরণ।
CSS ঠিক এই মডেল ব্যবহার করে। perspective: 400px মানে দর্শকের চোখ পর্দার (z = 0 সমতলের) ৪০০px সামনে। z = 0-তে থাকা জিনিস আসল মাপে দেখায়। একটি বিন্দু translateZ(200px) দিয়ে চোখের দিকে আনলে সেটি চোখ থেকে ২০০px দূরে, তাই প্রায় দ্বিগুণ বড় দেখায় (৪০০ ÷ ২০০)। পর্দার ভেতরে translateZ(-400px) পাঠালে দূরত্ব ৮০০px, মাপ অর্ধেক। ব্রাউজার এই অনুপাত matrix-এর একটি বিশেষ ঘর দিয়ে হিসাব করে, যাকে বলে perspective divide.
আর translateZ যদি perspective-এর সমান বা বেশি হয়? তখন জিনিসটি দর্শকের চোখের জায়গায় বা চোখের পেছনে চলে যায়। চোখের পেছনের জিনিস তো দেখা যায় না — ব্রাউজার সাধারণত তখন সেটিকে আর আঁকে না, বা কিনারায় অদ্ভুতভাবে কেটে দেয়। কিউব বানাতে গিয়ে কোনো মুখ “হারিয়ে গেলে” perspective-এর মান আর translateZ মিলিয়ে দেখুন।
একই গণিত চলে ফটোগ্রাফিতে (ছোট focal length মানে প্রবল বিকৃতি — স্টেপার ধাপ ৪-এর মতো), সিনেমার ভিজ্যুয়াল ইফেক্টে আর ৩D গেমে, যেখানে “field of view” আসলে perspective-এর আরেক নাম।
মূল উপলব্ধি: CSS-এর 3D কোনো জাদু নয় — পাঁচশো বছরের পুরোনো চিত্রকলার জ্যামিতি, যা আজ ব্রাউজার প্রতি ফ্রেমে GPU দিয়ে হিসাব করে। দর্শক কোথায় (perspective-origin) আর কত দূরে (perspective) — এই দুটিই সব কিছু ঠিক করে।
প্র ০৫একটি ঘূর্ণায়মান বা উল্টে যাওয়া ইফেক্ট দেখতে সুন্দর। তাহলে কেন অ্যাক্সেসিবিলিটির নির্দেশিকা নড়াচড়া নিয়ে এত সতর্ক?
কারণ চোখে দেখা নড়াচড়া শুধু চোখের ব্যাপার নয়। আমাদের ভেতরের কানের vestibular system শরীরের ভারসাম্য আর গতি টের পায়। মস্তিষ্ক চোখের তথ্য আর কানের তথ্য মেলায়; পর্দায় বড় কিছু ঘুরলে বা zoom হলে চোখ বলে “নড়ছি”, কান বলে “স্থির আছি”। এই অমিলে অনেকের মাথা ঘোরে, বমি ভাব হয় — গাড়িতে বই পড়লে যেমন হয়। vestibular সমস্যা, মাইগ্রেন বা মাথায় আঘাতের পর অনেকে এতে খুব সংবেদনশীল হয়ে পড়েন, আর অনুভূতিটি কয়েক ঘণ্টা থাকতে পারে।
বাস্তব একটি ঘটনা বিষয়টি সামনে আনে। ২০১৩ সালে iOS 7-এ Apple হোম স্ক্রিনে parallax আর zoom-এর বড় অ্যানিমেশন যোগ করে; বহু ব্যবহারকারী মাথা ঘোরার অভিযোগ করেন। এর পরে iPhone-এর Accessibility সেটিংসে “Reduce Motion” বিকল্প আসে। পরে ওয়েবেও সেই পছন্দ জানার উপায় হিসেবে আসে prefers-reduced-motion media feature, যা প্রথম চালু করে Safari; আজ এটি সব আধুনিক ব্রাউজারে চলে, আর Windows, Android, macOS — সব অপারেটিং সিস্টেমেই এর সেটিং আছে।
লেখার ক্ষেত্রে আরেকটি সমস্যা: ঘোরানো বা হেলানো লেখা পড়া ধীর, বিশেষ করে dyslexia বা দৃষ্টি-সমস্যা থাকলে, আর বাংলার মতো যুক্তাক্ষর-ভরা লিপিতে আরও কঠিন। আয়না-করা লেখা (scaleX(-1)) screen reader ঠিকঠাক পড়বে, কিন্তু চোখে দেখে পড়া মানুষ উল্টো দেখবেন — দুই ধরনের ব্যবহারকারী দুটি ভিন্ন অভিজ্ঞতা পাবেন।
WCAG 2.2-তে এ নিয়ে কয়েকটি নির্দেশনা আছে: 2.3.3 Animation from Interactions (AAA) — ব্যবহারকারীর কাজে শুরু হওয়া অপ্রয়োজনীয় নড়াচড়া বন্ধ করা যাবে; 2.2.2 Pause, Stop, Hide (A) — নিজে থেকে পাঁচ সেকেন্ডের বেশি চলা নড়াচড়া থামানোর উপায় থাকতে হবে; আর 2.3.1 — সেকেন্ডে তিনবারের বেশি ঝলকানি নিষেধ।
মূল উপলব্ধি: “সুন্দর” নির্ভর করে কে দেখছেন তার ওপর। ভালো ডিজাইনার নড়াচড়াকে একটি ঐচ্ছিক স্তর হিসেবে বানান — যিনি চান তিনি পাবেন, যিনি চান না তাঁর কাজ তাতে একটুও আটকাবে না।
অনুশীলন
আগে মনে মনে উত্তর ঠিক করুন, তারপর উপরের কোনো লাইভ এডিটরে লিখে মিলিয়ে দেখুন।
-
দূরত্ব অনুমান: একটি ৫০px বাক্সে (ক)
transform: translateX(100px) scale(2)আর (খ)transform: scale(2) translateX(100px). দুই ক্ষেত্রে বাক্সের মাঝখান কতটা ডানে সরবে? আর (গ)translate: 100px; scale: 2;দিলে?(ক) ১০০px. বাঁ থেকে ডানে: আগে ১০০px সরে, তারপর সেখানে দাঁড়িয়ে দ্বিগুণ হয় (নিজের মাঝখান ঘিরে)।
(খ) ২০০px. আগে দ্বিগুণ হয় — বাক্সের নিজের মাপকাঠিও দ্বিগুণ হয়ে যায় — তারপর সেই মাপকাঠিতে “১০০px” আসলে পর্দায় ২০০px.
(গ) ১০০px. আলাদা property-তে ক্রম সবসময় translate → rotate → scale, তাই ফল (ক)-এর মতো — আপনি যে ক্রমেই লিখুন।
-
লাফ দেওয়া মোডাল: সহকর্মী লিখেছেন
.box { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }আর.box:hover { transform: scale(1.05); }. মাউস রাখলে বাক্স ডান-নিচে লাফ দেয়। কেন? দুটি সমাধান দিন।hover rule জিতে পুরো
transformমান বদলে দেয় — নতুন মানেtranslate(-50%, -50%)আর নেই, তাই বাক্সের ওপর-বাঁ কোণ আবার মাঝখানে চলে আসে, অর্থাৎ বাক্স নিজের অর্ধেক মাপ ডানে আর নিচে সরে।সমাধান ১: hover-এ পুরো তালিকা আবার লিখুন:
transform: translate(-50%, -50%) scale(1.05).সমাধান ২ (আধুনিক): আলাদা property —
.box { translate: -50% -50%; }আর.box:hover { scale: 1.05; }. এখন একটি বদলালে অন্যটি অটুট থাকে। -
পালিয়ে যাওয়া চ্যাট বোতাম: একটি সাইটে নিচে-ডানে
position: fixedচ্যাট বোতাম ঠিকঠাক কাজ করছিল। পাতার মোড়ক<main class="app">-এ স্লাইড-ইন অ্যানিমেশনের জন্যtransform: translateY(0)যোগ করার পর বোতামটি পাতার সাথে স্ক্রল হয়ে চলে যায়। কেন, আর কীভাবে সারাবেন?noneছাড়া যেকোনো transform তার সব বংশধরের — fixed বাক্সেরও — containing block হয়ে যায়। চ্যাট বোতাম এখন viewport নয়,.app-এর নিচে-ডান কোণের সাপেক্ষে বসছে, আর.appতো পাতার সাথে স্ক্রল হয়।সমাধান: বোতামটি
.app-এর বাইরে, সরাসরি<body>-এর সন্তান হিসেবে রাখুন; অথবা অ্যানিমেশন শেষে transform সরিয়েnoneকরে দিন (যেমন animation-এ শেষ অবস্থাtransform: none). মোডাল হলে<dialog>ব্যবহার করুন — সেটি top layer-এ ওঠে, কোনো পূর্বপুরুষের transform তাকে আটকায় না। -
matrix পড়ুন: কোনো ঘর না চালিয়ে বলুন —
transform: rotate(90deg)দিলেgetComputedStyle()কোন matrix দেবে? আরscaleX(-1)দিলে? তারপর অংশ ৫-এর JavaScript টুলে মিলিয়ে দেখুন।2D ঘোরানোর matrix হলো
matrix(cos θ, sin θ, −sin θ, cos θ, 0, 0). θ = 90° হলে cos = 0, sin = 1, তাইmatrix(0, 1, -1, 0, 0, 0)(ব্রাউজার কখনো 0-এর বদলে6.12323e-17-এর মতো অতি ছোট সংখ্যা দেখাতে পারে — দশমিক হিসাবের সামান্য ত্রুটি)।scaleX(-1)-এ শুধু x-এর গুণক −1:matrix(-1, 0, 0, 1, 0, 0). খেয়াল করুন,aঋণাত্মক আরdধনাত্মক হলে ছবি আয়নার মতো উল্টে যায় — matrix দেখেই “mirror” চেনা যায়।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি 3D ফ্ল্যাশ কার্ড-বোতামে ক্লিক করার পর যা যা ঘটে, সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- JavaScript বোতামের
aria-pressed-কেtrueকরে .card[aria-pressed="true"] .innerনিয়মটি মেলে — মোড়কের নতুন মানrotateY(180deg)- transition শুরু হয় — মোড়ক খাড়া অক্ষ ঘিরে ধীরে ঘুরতে থাকে
- ৯০° পেরোতেই সামনের মুখ পেছন ফেরে আর
backface-visibility: hidden-এ লুকায়, পেছনের মুখ দেখা দেয় - ১৮০°-এ পৌঁছে পেছনের মুখ পুরো সোজা দেখা যায়
একটি flex সারির মাঝের বাক্সে transform: scale(1.5) দেওয়া হলো। পাশের বাক্সগুলোর কী হবে?
- পাশের বাক্সগুলো সরে গিয়ে বড় বাক্সের জন্য জায়গা ছেড়ে দেবে
- পাশের বাক্স একটুও নড়বে না; বড় বাক্সটি তাদের ওপর আংশিক উঠে যেতে পারে
- flex item-এ transform কাজ করে না, তাই কিছুই হবে না
- পুরো সারির উচ্চতা দেড় গুণ হয়ে যাবে
transform: translate(-50%, -50%)-এ % কিসের সাপেক্ষে মাপা হয়?
- parent element-এর চওড়া আর উচ্চতা
- ব্রাউজারের দৃশ্যমান জানালা (viewport)
- element-এর নিজের চওড়া আর উচ্চতা
- containing block-এর মাপ
একটি বাক্সে transform: rotate(90deg) translateX(100px) দেওয়া হলো (ডিফল্ট transform-origin). বাক্সটি আসল জায়গা থেকে কোন দিকে সরবে?
- ১০০px ডানে, আর সেখানে ৯০° ঘোরা
- ১০০px ওপরে
- কোথাও সরবে না, শুধু ঘুরবে
- ১০০px নিচে
একটি rule-এ লেখা: scale: 2; transform: skewX(10deg); translate: 40px; rotate: 15deg;. ব্রাউজার এগুলো কোন ক্রমে জোড়ে?
- translate → rotate → scale → transform
- যে ক্রমে লেখা: scale → transform → translate → rotate
- transform → translate → rotate → scale
- ব্রাউজারভেদে আলাদা, কোনো নির্দিষ্ট ক্রম নেই
3D কার্ড ফ্লিপে উল্টানোর পর সামনের মুখের লেখা আয়নার মতো উল্টো দেখা যাচ্ছে, পেছনের মুখ চাপা পড়ে আছে। কোন property সবচেয়ে সম্ভবত বাদ পড়েছে?
perspectivebackface-visibility: hiddentransform-origin: centerz-index
একটি position: fixed element-এর পূর্বপুরুষে transform: translateX(0) আছে। fixed element-টি কার সাপেক্ষে বসবে?
- সবসময় viewport-এর সাপেক্ষে — transform-এ কিছু যায় আসে না
<body>-এর সাপেক্ষে- transform-ওয়ালা সেই পূর্বপুরুষের সাপেক্ষে
- translateX(0) কোনো প্রভাব ফেলে না, তাই আগের মতোই
hover-এ কার্ড ভাসানোর অ্যানিমেশন যোগ করতে চান। অ্যাক্সেসিবিলিটি আর পারফরম্যান্স — দুই দিক থেকে কোনটি সবচেয়ে ভালো?
:hover-এposition: relative; top: -6px-কে transition- সব কার্ডে
will-change: transformদিয়ে বড় rotate অ্যানিমেশন margin-topকমিয়ে কার্ড ওপরে তোলা:hoverও:focus-visible-এtranslate-এর transition, আরprefers-reduced-motion-এ transition বন্ধ
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- transform লেআউট বদলায় না — বাক্স নিজের জায়গা রাখে, শুধু আঁকার সময় সরে, ঘোরে বা বড়-ছোট হয়; তাই অ্যানিমেশনে সস্তা আর মসৃণ।
translate(% = নিজের মাপ),rotate(deg/turn, ধনাত্মক = ঘড়ির কাঁটা),scale(ঋণাত্মক = আয়না),skew; inline element-এ কাজ করে না।- ক্রম গুরুত্বপূর্ণ — বাঁ থেকে ডানে, প্রতিটি ফাংশন নিজের বদলে যাওয়া অক্ষে; কেন্দ্র ঠিক করে
transform-origin; ভেতরে সব মিলে একটিmatrix(). - আলাদা
translate,rotate,scaleproperty (Baseline Widely available) সবসময় জোড়া লাগে translate → rotate → scale → transform ক্রমে। - 3D-তে
perspective(parent) বাperspective()(নিজে),preserve-3d,backface-visibility; আর মনে রাখুন stacking context, fixed-এর containing block আরprefers-reduced-motion.
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৪১ · ভিজ্যুয়াল ইফেক্ট — filter, blend mode, clip-path আর maskপরবর্তী পর্বছবি আর বাক্সের চেহারা বদলানোর আধুনিক CSS.
- HTML ও CSS · কোর্স হোম৫৯টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- পর্ব ২৪ · position আর z-index — relative, absolute, fixed, stickyঝালিয়ে নিনstacking context আর containing block — transform-এর পার্শ্বপ্রতিক্রিয়ার মূল।
- JavaScript · DOM-এর ভিত্তিপাশের ট্র্যাকJavaScript দিয়ে class বদলে transform চালানো আর মাপ পড়া।