اگر بتوانیم اندازه های مشارکت طراحان را بدون دردسر پیاده سازی کنیم چه؟ اگر بتوانیم نقاط لنگر سفارشی را تنظیم کنیم تا یک مقدار کاملاً پاسخگو ایجاد کنیم و با رویکرد اندازه سیال، گزینه های بیشتری به ما بدهد؟ اگر یک فرمول جادویی داشتیم که کل پروژه را کنترل و هماهنگ می کرد، چه می شد؟

اغلب اتفاق می افتد که من دو قالب را از طراح دریافت می کنم: یکی برای موبایل و دیگری برای دسکتاپ. اخیراً از خودم می‌پرسم چگونه می‌توانم فرآیند را خودکار کنم و نتیجه را بهینه کنم. چگونه اندازه های مشخص شده را به بهترین نحو پیاده سازی کنم؟ چگونه از یک نمای تبلت راحت اطمینان حاصل کنم؟ چگونه می توانم خروجی را برای صفحه نمایش های بزرگ بهینه کنم؟ چگونه می توانم به نسبت های شدید واکنش نشان دهم؟

قالب دسکتاپ و موبایل از طراح
قالب دسکتاپ و موبایل از طراح. (پیش نمایش بزرگ)

من دوست دارم بتوانم دو مقدار اندازه های مختلف (فونت، فاصله و غیره) را در پیکسل بخوانم و آنها را به عنوان آرگومان در تابعی وارد کنم که همه کارها را برای من انجام دهد. من می‌خواهم فرمول جادویی پاسخگو، FabUnit خود را ایجاد کنم.

وقتی در بهار شروع به کار روی این موضوع کردم و FabUnit را راه اندازی کردم، با این مقاله جالب آدریان مواجه شدم. در این میان روسلان و برشت نیز در این راستا تحقیق کرده و اندیشه های جالبی ارائه کرده اند.

چگونه می توانم قالب های طراحی را به بهترین نحو پیاده سازی کنم؟

من از نوشتن درخواست های رسانه ای برای هر مقدار خسته شده ام و می خواهم از جهش های طراحی جلوگیری کنم. تعمیر و نگهداری و نتیجه رضایت بخش نیست. بنابراین بهترین راه برای اجرای مشارکت طراح چیست؟

پرسش های رسانه ای با نقاط شکست
پرسش های رسانه ای با نقاط شکست. (پیش نمایش بزرگ)

در مورد اندازه مایعات چطور؟ ماشین حساب های دستی مانند وجود دارد اتوپیا یا حداقل – حداکثر – ماشین حساب.

اندازه سیال با حداقل و حداکثر
اندازه سیال با حداقل و حداکثر. (پیش نمایش بزرگ)

اما برای پروژه هایم، معمولاً به گزینه های تنظیمات بیشتری نیاز دارم. نمای تبلت اغلب خیلی کوچک به نظر می رسد و من نه می توانم به درگاه های دید بزرگتر و نه نسبت تصویر واکنش نشان دهم.

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

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

خط سفارشی پاسخگو بر اساس نقاط لنگر
خط سفارشی پاسخگو بر اساس نقاط لنگر. (پیش نمایش بزرگ)

محاسبات ما باید بر اساس چه ثابت هایی از مشخصات طراحی باشد؟

بیایید نگاهی دقیق تر به مثال قبلی خود بیندازیم:

ورودی از یک طراح با مشخصات اندازه و صفحه نمایش
ورودی از یک طراح با مشخصات اندازه و صفحه نمایش. (پیش نمایش بزرگ)

اندازه فونت بدنه باید باشد 16px روی موبایل و 22px روی دسکتاپ (در ادامه با راهنمای سبک کامل می پردازیم). اندازه تلفن همراه باید از شروع شود 375px و به طور مداوم به 1024px. تا 1440px، اندازه ها باید به صورت ایستا در حد مطلوب باقی بمانند. پس از آن، مقادیر باید به صورت خطی تا مقیاس بندی شوند 2000px، پس از آن max-wrapper اعمال می شود.

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

این به ما ثابت های زیر را می دهد که برای کل پروژه اعمال می شود:

Xf  375px       global      screen-min      
Xa  1024px      global      screen-opt-start
Xb  1440px      global      screen-opt-end
Xu  2000px      global      screen-max

اندازه فونت بدنه باید حداقل باشد 16px، در حالت ایده آل 22px. حداکثر اندازه فونت در 2000px باید به طور خودکار محاسبه شود:

Yf  16px        local       size-min        
Ya  22px
Yb  22px        local       size-opt
Yu  auto

بنابراین، در پایان روز، تابع من باید بتواند دو آرگومان بگیرد – در این مورد، 16 و 22.

fab-unit(16, 22);

محاسبات

اگر علاقه ای به استخراج ریاضی فرمول ندارید، به راحتی به بخش “پرش کنید”چگونه از FabUnit استفاده کنیم؟“.

بنابراین، بیایید شروع کنیم!

بیشتر بعد از پرش! ادامه مطلب زیر ↓

گیره های اصلی را تعریف کنید

ابتدا باید بفهمیم که کدام گیره اصلی را می خواهیم تنظیم کنیم.

clamp1: clamp(Yf, slope1, Ya)
clamp2: clamp(Yb, slope2, Yu)
تعریف گیره های اصلی بر اساس نقاط لنگر مشخص شده
تعریف گیره های اصلی بر اساس نقاط لنگر مشخص شده. (پیش نمایش بزرگ)

گیره ها را ترکیب و لانه کنید

حالا باید دو گیره را با هم ترکیب کنیم. این ممکن است کمی مشکل باشد. باید در نظر بگیریم که دو خط، slope1 و slope2، بسته به میزان شیب آنها می توانند روی یکدیگر بازنویسی کنند. از آنجایی که ما می دانیم slope2 باید 45 درجه یا 100٪ (m = 1) باشد، ما می توانیم پرس و جو کنیم که آیا slope1 بالاتر از 1 است. به این ترتیب بسته به نحوه تلاقی خطوط می توانیم یک گیره متفاوت تنظیم کنیم.

تجسم دو خط متقاطع
بسته به تفاوت بین دو اندازه، خطوط شیب می توانند قطع شوند – دو گیره می توانند در تضاد باشند. (پیش نمایش بزرگ)

اگر slope1 تندتر از slope2، گیره ها را به این صورت ترکیب می کنیم:

clamp(Yf, slope1, clamp(Yb, slope2, Yu))

اگر slope1 صاف تر از slope2، این محاسبه را انجام می دهیم:

clamp(clamp(Yf, slope1, Ya), slope2, Yu)

ترکیب شده:

steep-slope
  ? clamp(Yf, slope1, clamp(Yb, slope2, Yu))
  : clamp(clamp(Yf, slope1, Ya), slope2, Yu)

Maximum Wrapper را به صورت اختیاری تنظیم کنید

اگر ماکزیمم لفافی نداشته باشیم که طرح را بالاتر از عرض معینی منجمد کند چه؟

اگر قرار نیست مقدار حداکثری تنظیم شود، عملیات را تنظیم کنید
اگر قرار نیست مقدار حداکثری تنظیم شود، عملیات را تنظیم کنید. (پیش نمایش بزرگ)

ابتدا باید بفهمیم که کدام گیره اصلی را می خواهیم تنظیم کنیم.

clamp1: clamp(Yf, slope1, Ya)
max: max(Yb, slope2)

اگر slope1 تندتر از slope2:

clamp(Yf, slope1, max(Yb, slope2))

اگر slope1 صاف تر از slope2:

max(clamp(Yf, slope1, Ya), slope2)

محاسبه بدون لفاف – الاستیک به سمت بالا:

steep-slope
  ? clamp(Yf, slope1, max(Yb, slope2))
  : max(clamp(Yf, slope1, Ya), slope2)

ترکیبی، با بسته بندی حداکثر اختیاری (اگر صفحه نمایش حداکثر باشد Xu تنظیم شده است):

Xu
  ? steep-slope
    ? clamp(Yf, slope1, clamp(Yb, slope2, Yu))
    : max(clamp(Yf, slope1, Ya), Yu)
  : steep-slope
    ? clamp(Yf, slope1, max(Yb, slope2))
    : max(clamp(Yf, slope1, Ya), slope2)

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

مقادیر گمشده را محاسبه کنید

بیایید ببینیم که کدام مقادیر را به عنوان آرگومان دریافت می کنیم و اکنون باید کدام مقادیر را محاسبه کنیم:

steep-slope
  ? clamp(Yf, slope1, clamp(Yb, slope2, Yu))
  : max(clamp(Yf, slope1, Ya), Yu)

شیب تند
بله = Yb = 22 پیکسل
Yf = 16 پیکسل
شیب 1 = Mfa
شیب 2 = امبو
یو

برای تکمیل خط پاسخگو باید دو شیب و یک نقطه لنگر به طور خودکار محاسبه شود
برای تکمیل خط پاسخگو باید دو شیب و یک نقطه لنگر به طور خودکار محاسبه شود. (پیش نمایش بزرگ)
  • steep-slope
    بررسی کنید که آیا شیب Yf → Ya بالای شیب است Yb → Yu (m = 1) :
((Ya - Yf) / (Xa - Xf)) * 100 > 1
  • Mfa
    درون یابی خطی، از جمله محاسبه شیب Yf → Ya:
Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf)
  • Mbu
    درون یابی خطی بین Yb و Yu (شیب m = 1):
100vw / Xb * Yb
  • Yu
    محاسبه موقعیت Yu:
(Xu / Xb) * Yb

همه را با هم قرار دهید

Xu
  ? ((Ya - Yf) / (Xa - Xf)) * 100 > 1
    ? clamp(Yf, Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf), clamp(Yb, 100vw / Xb * Yb, (Xu / Xb) * Yb))
    : max(clamp(Yf, Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf), Ya), (Xu / Xb) * Yb)
  : ((Ya - Yf) / (Xa - Xf)) * 100 > 1
    ? clamp(Yf, Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf), max(Yb, 100vw / Xb * Yb))
    : max(clamp(Yf, Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf), Ya), 100vw / Xb * Yb)

بهتر است برخی از محاسبات را در متغیرها ذخیره کنیم:

steep-slope = ((Ya - Yf) / (Xa - Xf)) * 100 > 1
slope1 = Yf + (Ya - Yf) * (100vw - Xf) / (Xa - Xf)
slope2 = 100vw / Xb * Yb
Yu = (Xu / Xb) * Yb

Xu
  ? steep-slope
    ? clamp(Yf, slope1, clamp(Yb, slope2, Yu))
    : max(clamp(Yf, slope1, Ya), Yu)
  : steep-slope
    ? clamp(Yf, slope1, max(Yb, slope2))
    : max(clamp(Yf, slope1, Ya), slope2)

شامل نسبت ابعاد

از آنجا که ما اکنون می بینیم که گربه چگونه می پرد، خود را با یک کلوچه دیگر پذیرایی می کنیم. در مورد یک فرمت بسیار گسترده، به عنوان مثال چشم انداز دستگاه تلفن همراه، می خواهیم دوباره اندازه ها را کاهش دهیم. اینطوری دلپذیرتر و خواندنی تر است.

تجسم تأثیر منفی نسبت های شدید بر روی طرح رندر شده با صلیب در کادر قرمز
نسبت های بسیار زیاد می تواند تأثیر منفی بر طراحی رندر شده داشته باشد. (پیش نمایش بزرگ)

پس چه می شود اگر بتوانیم نسبت تصویر را در محاسبات خود لحاظ کنیم؟ در این مثال، می خواهیم اندازه ها را زمانی که صفحه نمایش از نسبت تصویر 16:9 عریض تر است، کوچک کنیم.

aspect-ratio = 16 / 9
screen-factor = min(100vw, 100vh * aspect-ratio)

در هر دو درونیابی شیب، ما به سادگی جایگزین می کنیم 100vw با فاکتور صفحه نمایش جدید

slope1 = Yf + (Ya - Yf) * (screen-factor - Xf) / (Xa - Xf)
slope2 = screen-factor / Xb * Yb

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

فرمول

screen-factor = min(100vw, 100vh * aspect-ratio)
steep-slope = ((Ya - Yf) / (Xa - Xf)) * 100 > 1
slope1 = Yf + (Ya - Yf) * (screen-factor - Xf) / (Xa - Xf)
slope2 = screen-factor / Xb * Yb
Yu = (Xu / Xb) * Yb

Xu
  ? steep-slope
    ? clamp(Yf, slope1, clamp(Yb, slope2, Yu))
    : max(clamp(Yf, slope1, Ya), Yu)
  : steep-slope
    ? clamp(Yf, slope1, max(Yb, slope2))
    : max(clamp(Yf, slope1, Ya), slope2)
FabUnit Visualizer که مقادیر تعریف شده توسط کاربر را می پذیرد، حداکثر اندازه محاسبه شده به صورت خودکار را نمایش می دهد و خط پاسخگوی مربوطه را ترسیم می کند.
یک دستی FabUnit Visualizer که مقادیر تعریف شده توسط کاربر را می پذیرد، حداکثر اندازه محاسبه شده به صورت خودکار را نمایش می دهد و خط پاسخگوی مربوطه را ترسیم می کند. (پیش نمایش بزرگ)

عملکرد

اکنون می توانیم فرمول را در راه اندازی خود ادغام کنیم. در این مقاله به نحوه پیاده سازی آن در Sass خواهیم پرداخت. دو تابع کمکی تضمین می‌کنند که مقادیر rem را به درستی خروجی می‌دهیم (من به جزئیات آن نمی‌پردازم). سپس نقاط لنگر و نسبت ابعاد را به عنوان ثابت (به ترتیب متغیرهای Sass) قرار می دهیم. در نهایت، نقاط مختصات فرمول خود را با نام متغیرها جایگزین می کنیم و FabUnit آماده استفاده است.

_fab-unit.scss

@use "sass:math";


/* Helper functions */

$rem-base: 10px;

@function strip-units($number) {
  @if (math.is-unitless($number)) {
      @return $number;
    } @else {
      @return math.div($number, $number * 0 + 1);
  }
}

@function rem($size){
  @if (math.compatible($size, 1rem) and not math.is-unitless($size)) {
    @return $size;
  } @else {
    @return math.div(strip-units($size), strip-units($rem-base)) * 1rem;
  }
}


/* Default values fab-unit 🪄 */

$screen-min: 375;
$screen-opt-start: 1024;
$screen-opt-end: 1440;
$screen-max: 2000;  // $screen-opt-end | int > $screen-opt-end | false
$aspect-ratio: math.div(16, 9);  // smaller values for larger aspect ratios


/* Magic function fab-unit 🪄 */

@function fab-unit(
    $size-min, 
    $size-opt, 
    $screen-min: $screen-min, 
    $screen-opt-start: $screen-opt-start, 
    $screen-opt-end: $screen-opt-end, 
    $screen-max: $screen-max,
    $aspect-ratio: $aspect-ratio
  ) {
  $screen-factor: min(100vw, 100vh * $aspect-ratio);
  $steep-slope: math.div(($size-opt - $size-min), ($screen-opt-start - $screen-min)) * 100 > 1;
  $slope1: calc(rem($size-min) + ($size-opt - $size-min) * ($screen-factor - rem($screen-min)) / ($screen-opt-start - $screen-min));
  $slope2: calc($screen-factor / $screen-opt-end * $size-opt);
  @if $screen-max {
    $size-max: math.div(rem($screen-max), $screen-opt-end) * $size-opt;
    @if $steep-slope {
      @return clamp(rem($size-min), $slope1, clamp(rem($size-opt), $slope2, $size-max));
    } @else {
      @return clamp(clamp(rem($size-min), $slope1, rem($size-opt)), $slope2, $size-max);
    }
  } @else {
    @if $steep-slope {
      @return clamp(rem($size-min), $slope1, max(rem($size-opt), $slope2));
    } @else {
      @return max(clamp(rem($size-min), $slope1, rem($size-opt)), $slope2);
    }
  }
}

چگونه از FabUnit استفاده کنیم؟

کار تمام شده است، اکنون ساده است. راهنمای سبک از مثال ما را می توان در کوتاه ترین زمان اجرا کرد:

راهنمای سبک از یک طراح با تمام مشخصات اندازه برای موبایل و دسکتاپ
راهنمای سبک از یک طراح با تمام مشخصات اندازه برای موبایل و دسکتاپ. (پیش نمایش بزرگ)

ما مقادیر مرتبط را از راهنمای سبک می خوانیم و آنها را به عنوان آرگومان به FabUnit ارسال می کنیم: fab-unit(16, 22).

style.scss

@import "fab-unit";


/* overwrite default values 🪄 */
$screen-max: 1800;


/* Style guide variables fab-unit 🪄 */

$fab-font-size-body: fab-unit(16, 22);
$fab-font-size-body-small: fab-unit(14, 16);

$fab-font-size-h1: fab-unit(60, 160);
$fab-font-size-h2: fab-unit(42, 110);
$fab-font-size-h3: fab-unit(28, 60);

$fab-space-s: fab-unit(20, 30);
$fab-space-m: fab-unit(40, 80);
$fab-space-l: fab-unit(60, 120);
$fab-space-xl: fab-unit(80, 180);


/* fab-unit in action 🪄 */

html {
  font-size: 100% * math.div(strip-units($rem-base), 16);
}

body {
  font-size: $fab-font-size-body;
}

.wrapper {
  max-width: rem($screen-max);
  margin-inline: auto;
  padding: $fab-space-m;
}

h1 {
  font-size: $fab-font-size-h1;
  border-block-end: fab-unit(2, 10) solid plum;
}

…

p {
  margin-block: $fab-space-s;
}

…
/* other use cases for calling fab-unit 🪄 */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(fab-unit(200, 500), 1fr));
  gap: $fab-space-m;
}

.thing {
  flex: 0 0 fab-unit(20, 30);
  height: fab-unit(20, 36, 660, 800, 1600, 1800);  /* min, opt, … custom anchor points */
}

اکنون می توانیم با فراخوانی خط پاسخگو را رسم کنیم fab-unit() و فقط دو اندازه، حداقل و بهینه را مشخص کنید. می‌توانیم اندازه‌های فونت، بالشتک‌ها، حاشیه‌ها و شکاف‌ها، ارتفاع‌ها و عرض‌ها را کنترل کنیم، و حتی اگر بخواهیم ستون‌های شبکه‌ای و طرح‌بندی‌های انعطاف‌پذیر را با آن تعریف کنیم. همچنین می توانیم نقاط لنگر از پیش تعریف شده را به صورت محلی جابجا کنیم.

سازگاری بهینه با اندازه های مختلف ویوپورت ها و نسبت های تصویر با FabUnit
سازگاری بهینه با اندازه های مختلف ویوپورت ها و نسبت های تصویر با FabUnit. (پیش نمایش بزرگ)

بیایید به خروجی کامپایل شده نگاهی بیندازیم:

…
font-size: clamp(clamp(1.3rem, 1.3rem + 2 * (min(100vw, 177.7777777778vh) - 37.5rem) / 649, 1.5rem), min(100vw, 177.7777777778vh) / 1440 * 15, 2.0833333333rem);
…

و خروجی محاسبه شده:

font-size: 17.3542px

نگرانی های دسترسی

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

نتیجه

اکنون ما یک تابع ایجاد کرده ایم که همه کارها را برای ما انجام می دهد. این یک مقدار حداقل و بهینه را می گیرد و با در نظر گرفتن عرض صفحه، نسبت ابعاد و نقاط لنگر مشخص، یک محاسبه را به ویژگی CSS ما می اندازد – یک فرمول واحد که کل پروژه را هدایت می کند. بدون درخواست رسانه، بدون نقطه شکست، بدون جهش طراحی.

FabUnit ارائه شده در اینجا بر اساس تجربه شخصی من است و برای اکثر پروژه های من بهینه شده است. من در زمان زیادی صرفه جویی می کنم و از نتیجه راضی هستم. ممکن است شما و تیمتان رویکرد دیگری داشته باشید و بنابراین نیازهای دیگری برای FabUnit داشته باشید. خیلی خوب است اگر اکنون بتوانید FabUnit خود را مطابق با نیاز خود ایجاد کنید.

خوشحال می شوم اگر رویکرد من ایده های جدیدی را به شما القا کند. مفتخرم اگر مستقیماً از آن استفاده کنید بسته npm از FabUnit از این مقاله برای پروژه های شما.

متشکرم! 🙏🏻

مرسی الی، رومن، پاتریک، فیدی.

سرمقاله Smashing
(yk, il)