পাঁচ বছর আগে আমি কিভাবে ষড়ভুজ আকারের একটি প্রতিক্রিয়াশীল গ্রিড তৈরি করতে একটি নিবন্ধ প্রকাশ করেছি। এটি একমাত্র প্রযুক্তি যার জন্য মিডিয়া প্রশ্ন বা জাভাস্ক্রিপ্টের প্রয়োজন ছিল না। এটি যেকোন সংখ্যক আইটেমের সাথে কাজ করে, যার ফলে আপনি সহজেই CSS ভেরিয়েবল ব্যবহার করে আকার এবং ব্যবধান নিয়ন্ত্রণ করতে পারবেন।
আমি ব্যবহার করছি float, inline-blockসেটিং font-size সমান 02026 সালে, এটি কিছুটা হ্যাকনিড এবং পুরানো বলে মনে হতে পারে। সত্যিই নয় কারণ এই পদ্ধতিটি সূক্ষ্ম কাজ করে এবং ভালভাবে সমর্থিত, তবে আমরা কি আধুনিক বৈশিষ্ট্যগুলি ব্যবহার করে আরও ভাল করতে পারি? পাঁচ বছরে, অনেক কিছু পরিবর্তিত হয়েছে এবং আমরা উপরের বাস্তবায়নকে উন্নত করতে পারি এবং এটিকে কম হ্যাকি করতে পারি!
সমর্থন শুধুমাত্র Chrome-এ সীমাবদ্ধ কারণ এই প্রযুক্তি সম্প্রতি প্রকাশিত বৈশিষ্ট্যগুলি ব্যবহার করে৷ corner-shape, sibling-index()এবং ইউনিট বিভাগ।
CSS কোড ছোট এবং গতবারের তুলনায় কম ম্যাজিক নম্বর আছে। আপনি কিছু জটিল গণনাও পাবেন যা আমরা একসাথে বিশ্লেষণ করব।
এই নতুন ডেমোতে ডুব দেওয়ার আগে, আমি দৃঢ়ভাবে আমার আগের নিবন্ধটি পড়ার সুপারিশ করছি। এটি বাধ্যতামূলক নয়, তবে এটি আপনাকে উভয় পদ্ধতির তুলনা করতে এবং গত পাঁচ বছরে CSS কতটা (এবং দ্রুত) বিকশিত হয়েছে তা অনুভব করতে দেয় নতুন বৈশিষ্ট্যগুলি প্রবর্তন করে যা এর মতো কঠিন জিনিসগুলিকে সহজ করে তোলে।
ষড়ভুজ আকৃতি
ষড়ভুজ আকৃতি দিয়ে শুরু করা যাক, যা আমাদের গ্রিডের প্রধান উপাদান। আগে আমাকে নির্ভর করতে হতো clip-path: polygon() এটি তৈরি করতে:
.hexagon {
--s: 100px;
width: var(--s);
height: calc(var(--s) * 1.1547);
clip-path: polygon(0% 25%, 0% 75%, 50% 100%, 100% 75%, 100% 25%, 50% 0%);
}
কিন্তু এখন, আমরা নতুন উপর নির্ভর করতে পারেন corner-shape সম্পদ যা একসাথে কাজ করে border-radius সম্পত্তি:
.hexagon {
width: 100px;
aspect-ratio: cos(30deg);
border-radius: 50% / 25%;
corner-shape: bevel;
}
আমরা যেভাবে এলিমেন্ট তৈরি করেছি তার থেকে অনেক সহজ এবং বোনাস হিসেবে, আমরা কাজ ছাড়াই আকৃতিতে সীমানা যোগ করতে পারি!
corner-shape সম্পত্তি হল প্রথম আধুনিক সুবিধা যার উপর আমরা নির্ভর করছি। এটি সিএসএস আকার তৈরি করাকে ঐতিহ্যগত পদ্ধতি ব্যবহার করার চেয়ে অনেক সহজ করে তোলে, যেমন clip-path. আপনি এখনও এটি ব্যবহার চালিয়ে যেতে পারেন clip-path পদ্ধতি, অবশ্যই, আরও ভাল সমর্থনের জন্য (এবং যদি আপনার উপাদানটিতে একটি সীমানা প্রয়োজন না হয়), তবে এখানে আরও আধুনিক বাস্তবায়ন রয়েছে:
.hexagon {
width: 100px;
aspect-ratio: cos(30deg);
clip-path: polygon(-50% 50%,50% 100%,150% 50%,50% 0);
}
বহুভুজের ভিতরে কম পয়েন্ট আছে, এবং আমরা জাদু সংখ্যা পরিবর্তন করেছি 1.1547 একসাথে aspect-ratio ঘোষণা। আমি আকারের কোডে বেশি সময় ব্যয় করব না, তবে আপনি যদি আরও উদাহরণ সহ একটি বিশদ ব্যাখ্যা চান তবে আমি এখানে দুটি নিবন্ধ লিখেছি:
প্রতিক্রিয়াশীল গ্রিড
এখন যেহেতু আমাদের আকার আছে, আসুন গ্রিড তৈরি করি। এটিকে “গ্রিড” বলা হয়, তবে আমি একটি ফ্লেক্সবক্স কনফিগারেশন ব্যবহার করতে যাচ্ছি:
.container {
--s: 120px; /* size */
--g: 10px; /* gap */
display: flex;
gap: var(--g);
flex-wrap: wrap;
}
.container > * {
width: var(--s);
aspect-ratio: cos(30deg);
border-radius: 50% / 25%;
corner-shape: bevel;
}
এখনও অভিনব কিছুই. সেখান থেকে, সারিগুলির মধ্যে ওভারল্যাপ তৈরি করতে আমরা সমস্ত আইটেমের নীচে একটি মার্জিন যুক্ত করি:
.container > * {
margin-bottom: calc(var(--s)/(-4*cos(30deg)));
}
চূড়ান্ত ধাপ হল জোড় সারির প্রথম আইটেমে একটি বাম মার্জিন যোগ করা (অর্থাৎ, 2য়, 4র্থ, 6 ম, এবং তাই)। এই মার্জিন একটি নিখুঁত গ্রিড অর্জন করতে সারিগুলির মধ্যে রূপান্তর তৈরি করবে।
বলা হচ্ছে, এটি সহজ শোনাচ্ছে, কিন্তু এটি সবচেয়ে কঠিন অংশ যেখানে আমাদের জটিল গণনার প্রয়োজন। গ্রিডটি প্রতিক্রিয়াশীল, তাই আমরা যে "প্রথম" অবজেক্টটি খুঁজছি সেটি পাত্রের আকার, বস্তুর আকার, ব্যবধান ইত্যাদির উপর নির্ভর করে যেকোনো বস্তু হতে পারে।
একটি চিত্র দিয়ে শুরু করা যাক:

প্রতিক্রিয়ার উপর নির্ভর করে আমাদের গ্রিডের দুটি দিক থাকতে পারে। আমাদের হয় সমস্ত সারিতে একই সংখ্যক আইটেম থাকতে পারে (উপরের ছবিতে গ্রিড 1) অথবা পরপর দুটি সারির মধ্যে একটি আইটেমের পার্থক্য (গ্রিড 2)। N এবং M ভেরিয়েবল সারি আইটেম সংখ্যা প্রতিনিধিত্ব করে. গ্রিড 1 এ আমরা আছে N = Mএবং গ্রিড 2 এ আমরা আছে M = N - 1.
গ্রিড 1-এ, বাম মার্জিন আইটেমগুলি হল 6, 16, 26, ইত্যাদি, এবং গ্রিড 2-এ সেগুলি হল 7, 18, 29, ইত্যাদি৷ আসুন সেই সংখ্যাগুলির পিছনে যুক্তি সনাক্ত করার চেষ্টা করি৷
উভয় গ্রিডে প্রথম আইটেমটি (6 বা 7) দ্বিতীয় সারির প্রথম আইটেম, তাই এই আইটেমটি N + 1. দ্বিতীয় আইটেমটি (16 বা 18) তৃতীয় সারিতে প্রথম, তাই এই আইটেমটি N + M + N + 1. তৃতীয় আইটেম (26 বা 29) হয় N + M + N + M + N + 1. আপনি যদি ঘনিষ্ঠভাবে তাকান, আপনি একটি প্যাটার্ন দেখতে পাবেন যা আমরা নিম্নলিখিত সূত্র ব্যবহার করে প্রকাশ করতে পারি:
N*i + M*(i - 1) + 1
…কোথায় i একটি ধনাত্মক পূর্ণসংখ্যা (শূন্য বাদে)। আমরা যে বস্তুগুলি খুঁজছি তা নিম্নলিখিত pseudocode ব্যবহার করে পাওয়া যাবে:
for(i = 0; i< ?? ;i++) {
index = N*i + M*(i - 1) + 1
Add margin to items[index]
}
যাইহোক, আমাদের সিএসএসে লুপ নেই, তাই আমাদের আলাদা কিছু করতে হবে। আমরা নতুন ব্যবহার করে প্রতিটি আইটেমের সূচক পেতে পারি sibling-index() উদযাপন। যুক্তি হল সেই সূচকটি পূর্ববর্তী সূত্রটিকে সম্মান করে কিনা তা পরীক্ষা করা।
এটি লেখার পরিবর্তে:
index = N*i + M*(i - 1) + 1
...এর প্রকাশ করা যাক i সূচক ব্যবহার করে:
i = (index - 1 + M)/(N + M)
আমরা তা জানি i একটি ধনাত্মক পূর্ণসংখ্যা (শূন্য ব্যতীত), তাই প্রতিটি আইটেমের জন্য, আমরা তার সূচক পাই এবং পরীক্ষা করি যদি (index - 1 + M)/(N + M) একটি ধনাত্মক পূর্ণসংখ্যা। তার আগে, আসুন আইটেম সংখ্যা গণনা করা যাক, N এবং M.
প্রতি লাইনে আইটেমের সংখ্যা গণনা করা সেই লাইনে কতগুলি আইটেম ফিট হতে পারে তা গণনা করার সমান।
N = round(down,container_size / item_size);
ধারকটির আকারকে আইটেমের আকার দিয়ে ভাগ করলে আমাদের একটি সংখ্যা পাওয়া যায়। যদি আমরা round()`এটিকে নিকটতম পূর্ণসংখ্যায় বৃত্তাকার করে, আমরা প্রতি সারিতে আইটেমের সংখ্যা পাই। কিন্তু আমাদের আইটেমগুলির মধ্যে পার্থক্য রয়েছে, তাই আমাদের এটিকে সূত্রে অন্তর্ভুক্ত করতে হবে:
N = round(down, (container_size + gap)/ (item_size + gap));
আমরা একই কাজ Mকিন্তু এই সময় আমাদের সারির প্রথম আইটেমটিতে প্রয়োগ করা বাম মার্জিনের যত্ন নেওয়া দরকার:
M = round(down, (container_size + gap - margin_left)/ (item_size + gap));
আসুন আরও ঘনিষ্ঠভাবে দেখে নেওয়া যাক এবং পরবর্তী চিত্রে সেই মার্জিনের মান সনাক্ত করি:

এটি একটি বস্তুর আকারের অর্ধেক এবং অর্ধেক পার্থক্যের সমান:
M = round(down, (container_size + gap - (item_size + gap)/2)/(item_size + gap));
M = round(down, (container_size - (item_size - gap)/2)/(item_size + gap));
আইটেম আকার এবং ব্যবধান ব্যবহার করে সংজ্ঞায়িত করা হয় --s এবং --g পরিবর্তনশীল, কিন্তু ধারক আকার সম্পর্কে কি? আমরা কন্টেইনার কোয়েরি সত্তার উপর নির্ভর করতে এবং ব্যবহার করতে পারি 100cqw.
এখন পর্যন্ত CSS ব্যবহার করে আমরা যা পেয়েছি তা লিখি:
.container {
--s: 120px; /* size */
--g: 10px; /* gap */
container-type: inline-size; /* we make it a container to use 100cqw */
}
.container > * {
--_n: round(down,(100cqw + var(--g))/(var(--s) + var(--g)));
--_m: round(down,(100cqw - (var(--s) - var(--g))/2)/(var(--s) + var(--g)));
--_i: calc((sibling-index() - 1 + var(--_m))/(var(--_n) + var(--_m)));
margin-left: ???; /* We're getting there! */
}
আমরা ব্যবহার করতে পারি mod(var(--_i),1) যদি পরীক্ষা করতে হয় --_i একটি পূর্ণসংখ্যা। যদি এটি একটি পূর্ণসংখ্যা হয়, ফলাফলটি 0 এর সমান। অন্যথায়, এটি 0 এবং 1 এর মধ্যে একটি মানের সমান।
আমরা আরেকটি ভেরিয়েবল প্রবর্তন করতে পারি এবং নতুন ব্যবহার করতে পারি if() উদযাপন !
.container {
--s: 120px; /* size */
--g: 10px; /* gap */
container-type: inline-size; /* we make it a container to use 100cqw */
}
.container > * {
--_n: round(down,(100cqw + var(--g))/(var(--s) + var(--g)));
--_m: round(down,(100cqw - (var(--s) - var(--g))/2)/(var(--s) + var(--g)));
--_i: calc((sibling-index() - 1 + var(--_m))/(var(--_n) + var(--_m)));
--_c: mod(var(--_i),1);
margin-left: if(style(--_c: 0) calc((var(--s) + var(--g))/2) else 0;);
}
টাডা !
এটি লক্ষ্য করা গুরুত্বপূর্ণ যে আপনাকে ভেরিয়েবলটি নিবন্ধন করতে হবে --_c রূপান্তরযোগ্য ব্যবহার করে @property তুলনা করতে সক্ষম হতে (আমি এই সম্পর্কে আরও লিখি "কিভাবে সঠিকভাবে ব্যবহার করতে হয় if()সিএসএসে")
এই জন্য একটি ভাল ব্যবহার ক্ষেত্রে if()কিন্তু আমরা এটি ভিন্নভাবে করতে পারি:
--_c: round(down, 1 - mod(var(--_i), 1));
mod() ফাংশনটি আমাদের 0 এবং 1 এর মধ্যে একটি মান দেয়, যেখানে 0 হল আমরা যে মানটি চাই। -1*mod() আমাদের -1 এবং 0 এর মধ্যে একটি মান দেয়। 1 - mod() আমাদের 0 এবং 1 এর মধ্যে একটি মান দেয়, কিন্তু এই সময় আমাদের 1 প্রয়োজন। আমরা আবেদন করি round() গণনার জন্য, এবং ফলাফল 0 বা 1 হবে --_c ভেরিয়েবলটি এখন একটি বুলিয়ান ভেরিয়েবল যা আমরা সরাসরি গণনায় ব্যবহার করতে পারি।
margin-left: calc(var(--_c) * (var(--s) + var(--g))/2);
যদি --_c 1 এর সমান, আমরা মার্জিন পাই। অন্যথায়, মার্জিন 0 এর সমান। এবার আপনাকে ভেরিয়েবল ব্যবহার করে নিবন্ধন করতে হবে না @property. আমি ব্যক্তিগতভাবে এই পদ্ধতি পছন্দ করি কারণ এটি কম কোড প্রয়োজন, কিন্তু if() পদ্ধতিটিও আকর্ষণীয়।
আমাকে কি সেই সব সূত্র মুখস্ত করতে হবে?! এটা খুব বেশি!
না, তুমি তা করো না। আমি এটির পিছনে গণিতের একটি বিশদ ব্যাখ্যা দেওয়ার চেষ্টা করেছি, তবে গ্রিডগুলির সাথে কাজ করার জন্য এটি বোঝা বাধ্যতামূলক নয়। আপনাকে যা করতে হবে তা হল ভেরিয়েবলগুলি আপডেট করা যা আকার এবং ব্যবধান নিয়ন্ত্রণ করে। বাম মার্জিন সেট করে এমন অংশটি স্পর্শ করার দরকার নেই। একই কোড স্ট্রাকচার আরও আকারের সাথে কীভাবে কাজ করতে পারে তাও আমরা অন্বেষণ করব!
আরো উদাহরণ
সাধারণ ব্যবহারের ক্ষেত্রে ষড়ভুজ আকৃতি কিন্তু অন্যান্য আকার সম্পর্কে কি? উদাহরণস্বরূপ, আমরা একটি রম্বস বিবেচনা করতে পারি এবং এর জন্য, আমরা কেবল কোডটি সামঞ্জস্য করি যা আকৃতি নিয়ন্ত্রণ করে।
এই থেকে:
.container > * {
aspect-ratio: cos(30deg);
border-radius: 50% / 25%;
corner-shape: bevel;
margin-bottom: calc(var(--s)/(-4*cos(30deg)));
}
…এর জন্য:
.container > * {
aspect-ratio: 1;
border-radius: 50%;
corner-shape: bevel;
margin-bottom: calc(var(--s)/-2);
}
রম্বস আকারের একটি প্রতিক্রিয়াশীল গ্রিড – কোনো প্রচেষ্টা ছাড়াই! আসুন একটি অষ্টভুজ চেষ্টা করি:
.container > * {
aspect-ratio: 1;
border-radius: calc(100%/(2 + sqrt(2)));
corner-shape: bevel;
margin-bottom: calc(var(--s)/(-1*(2 + sqrt(2))));
}
সম্পর্কে একটি অষ্টভুজের জন্য, আমাদের সামঞ্জস্য করতে হবে gap কারণ আমাদের বস্তুর মধ্যে আরও অনুভূমিক স্থান প্রয়োজন:
.container {
--g: calc(10px + var(--s)/(sqrt(2) + 1));
gap: 10px var(--g);
}
পরিবর্তনশীল --g আকৃতির একটি অংশ রয়েছে var(--s)/(sqrt(2) + 1) এবং এটি সারি ফাঁক হিসাবে প্রয়োগ করা হয়, যখন কলামের ফাঁক একই রাখা হয় (10px)
সেখান থেকে, আমরা অন্য ধরনের হেক্সাগন গ্রিডও পেতে পারি:
এবং কেন চেনাশোনা একটি গ্রিড খুব না? এটি এখানে:
আপনি দেখতে পাচ্ছেন, আমরা জটিল গণনাগুলি স্পর্শ করিনি যা এই উদাহরণগুলির মধ্যে কোনোটিতে বাম মার্জিন সেট করে। আমরা শুধু এটা সঙ্গে খেলা ছিল border-radius এবং aspect-ratio সাইজ এবং ফাইন-টিউন ওভারল্যাপ নিয়ন্ত্রণ করতে নিচের মার্জিন সামঞ্জস্য করার বৈশিষ্ট্য। কিছু ক্ষেত্রে, আমাদের অনুভূমিক ফাঁক সামঞ্জস্য করতে হবে।
উপসংহার
আমি এই নিবন্ধটি অন্য ডেমো দিয়ে শেষ করব যা আপনার জন্য একটি ছোট হোমওয়ার্ক হিসাবে কাজ করবে:
এবার, পরিবর্তনটি জোড় সারির পরিবর্তে বিজোড় সারিতে প্রয়োগ করা হয়েছে। আমাকে একটু ব্যায়াম হিসাবে আপনার জন্য কোড বিশ্লেষণ করা যাক. আমি যে পরিবর্তন করেছি তা সনাক্ত করার চেষ্টা করুন এবং এর পিছনে যুক্তি কী (প্রম্পট: এই নতুন কনফিগারেশনটি ব্যবহার করে আবার গণনার পদক্ষেপগুলি সম্পাদন করার চেষ্টা করুন।)