نحوه استفاده Nordhealth از ویژگی‌های سفارشی در «عناصر وب»

مزایای استفاده از ویژگی‌های سفارشی در سیستم‌های طراحی و کتابخانه‌های مؤلفه.

David Darnes
David Darnes

من دیو هستم و توسعه‌دهنده ارشد سمت کاربر در Nordhealth هستم. من روی طراحی و توسعه سیستم طراحی Nord کار می‌کنم که شامل ساختن «مؤلفه‌های وب» برای کتابخانه مؤلفه‌های ما است. می‌خواستم نحوه حل مشکلات مربوط به سبک‌دهی مؤلفه‌های وب بااستفاده از ویژگی‌های سفارشی CSS و برخی مزایای دیگر استفاده از ویژگی‌های سفارشی در سیستم‌های طراحی و کتابخانه‌های مؤلفه را هم‌رسانی کنم.

چگونه «عناصر وب» را می‌سازیم

برای ساختن «مؤلفه‌های وب» خودمان، از Lit استفاده می‌کنیم، کتابخانه‌ای که کد متن استاندارد زیادی مثل حالت، سبک‌های محدودشده، الگو، و غیره ارائه می‌دهد. Lit نه‌تنها سبک است، بلکه بر روی میاناهای برنامه‌سازی کاربردی جاوا اسکریپت بومی ساخته شده است، به این معنی که می‌توانیم یک بسته کد ساده ارائه دهیم که از ویژگی‌هایی که مرورگر درحال‌حاضر دارد بهره می‌برد.


import {html, css, LitElement} from 'lit';

export class SimpleGreeting extends LitElement {
  static styles = css`:host { color: blue; font-family: sans-serif; }`;

  static properties = {
    name: {type: String},
  };

  constructor() {
    super();
    this.name = 'there';
  }

  render() {
    return html`

Hey ${this.name}, welcome to Web Components!

`
; } } customElements.define('simple-greeting', SimpleGreeting);
عنصر وب نوشته‌شده با Lit.

اما جذاب‌ترین چیز درباره Web Components این است که تقریباً با هر چارچوب JavaScript موجود، یا حتی بدون هیچ چارچوبی، کار می‌کنند. وقتی به بسته اصلی جاوا اسکریپت در صفحه ارجاع داده می‌شود، استفاده از «عنصر وب» بسیار شبیه به استفاده از عنصر HTML بومی است. تنها نشانه واقعی که نشان می‌دهد این عنصر یک عنصر HTML بومی نیست، خط تیره ثابت درون برچسب‌ها است که استانداردی برای نشان دادن این موضوع به مرورگر است که این یک «مؤلفه وب» است.

کپسوله‌سازی سبک DOM سایه

همان‌طور که عناصر HTML بومی دارای Shadow DOM هستند، «مؤلفه‌های وب» نیز این ویژگی را دارند. Shadow DOM یک درخت پنهان از گره‌ها در یک عنصر است. بهترین راه برای تجسم این موضوع، باز کردن بازرس وب و روشن کردن گزینه «نمایش درخت DOM سایه» است. پس‌از انجام این کار، سعی کنید به عنصر ورودی بومی در بازرس نگاه کنید—اکنون این گزینه را خواهید داشت که آن ورودی را باز کنید و همه عناصر درون آن را ببینید. حتی می‌توانید این کار را با یکی از «مؤلفه‌های وب» ما امتحان کنید—مؤلفه ورودی سفارشی ما را بازرسی کنید تا Shadow DOM آن را ببینید.

‫shadow DOM بازرسی‌شده در DevTools.
نمونه‌ای از Shadow DOM در عنصر ورودی نوشتار معمولی و در «مؤلفه وب» ورودی Nord ما.

یکی از مزایای (یا معایب، بسته به دیدگاه شما) Shadow DOM، کپسوله‌سازی سبک است. اگر CSS را در «مؤلفه وب» خود بنویسید، آن سبک‌ها نمی‌توانند به بیرون نشت کنند و بر صفحه اصلی یا عناصر دیگر تأثیر بگذارند؛ آن‌ها کاملاً در مؤلفه قرار دارند. علاوه‌براین، CSS نوشته‌شده برای صفحه اصلی یا «مؤلفه وب» اصلی نمی‌تواند به «مؤلفه وب» شما نشت کند.

این کپسوله‌سازی سبک‌ها مزیتی در کتابخانه مؤلفه ما است. این کار به ما اطمینان بیشتری می‌دهد که وقتی کسی از یکی از مؤلفه‌های ما استفاده می‌کند، صرف‌نظر از سبک‌های اعمال‌شده روی صفحه اصلی، همان‌طور که ما درنظر داشتیم به‌نظر برسد. و برای اطمینان بیشتر، all: unset; را به ریشه یا «میزبان» همه مؤلفه‌های وب خود اضافه می‌کنیم.


:host {
  all: unset;
  display: block;
  box-sizing: border-box;
  text-align: start;
  /* ... */
}
برخی‌از کدهای متن استاندارد مؤلفه که روی ریشه سایه یا انتخابگر میزبان اعمال می‌شوند.

اما اگر فردی که از «مؤلفه وب» شما استفاده می‌کند دلیل موجهی برای تغییر برخی‌از سبک‌ها داشته باشد چه؟ شاید خطی از نوشتار وجود دارد که به‌دلیل بافتش به کنتراست بیشتری نیاز دارد، یا حاشیه‌ای باید ضخیم‌تر باشد؟ اگر هیچ سبکی نتواند وارد مؤلفه شما شود، چگونه می‌توانید قفل آن گزینه‌های سبک‌بندی را باز کنید؟

اینجا است که ویژگی‌های سفارشی CSS وارد می‌شوند.

ویژگی‌های سفارشی CSS

دارایی‌های سفارشی نام بسیار مناسبی دارند—آن‌ها دارایی‌های CSS هستند که می‌توانید کاملاً خودتان نام‌گذاری کنید و هر مقداری را که لازم است اعمال کنید. تنها شرط این است که دو خط تیره به ابتدای آن‌ها اضافه کنید. وقتی ویژگی سفارشی‌تان را اعلام کردید، می‌توانید از مقدار آن در CSS بااستفاده از تابع var() استفاده کنید.


:root {
  --n-color-accent: rgb(53, 89, 199);
  /* ... */
}

.n-color-accent-text {
  color: var(--n-color-accent);
}
نمونه‌ای از چارچوب CSS ما از یک نشانه طراحی به‌عنوان یک دارایی سفارشی و استفاده از آن در یک کلاس کمکی.

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

این توانایی برای به‌ارث بردن ویژگی‌های سفارشی، بااستفاده از تابع var()، روشی است که ازطریق آن به Shadow DOM «مؤلفه‌های وب» خود نفوذ می‌کنیم و به توسعه‌دهندگان اجازه می‌دهیم کنترل دقیق‌تری هنگام سبک‌دهی به مؤلفه‌های ما داشته باشند.

مشخصات سفارشی در «عنصر وب Nord»

هرگاه مؤلفه‌ای را برای سیستم طراحی‌مان توسعه می‌دهیم، رویکردی متفکرانه نسبت به CSS آن اتخاذ می‌کنیم—هدف ما کدی ساده اما بسیار قابل‌نگهداری است. نشان‌های طراحی ما به‌عنوان ویژگی‌های سفارشی در چارچوب CSS اصلی ما در عنصر ریشه تعریف شده‌اند.


:root {
  --n-space-m: 16px;
  --n-space-l: 24px;
  /* ... */
  --n-color-background: rgb(255, 255, 255);
  --n-color-border: rgb(216, 222, 228);
  /* ... */
}
ویژگی‌های سفارشی CSS که در گزینشگر ریشه تعریف شده‌اند.

سپس این مقادیر توکن در مؤلفه‌های ما ارجاع داده می‌شوند. در برخی موارد، مقدار را مستقیماً روی دارایی CSS اعمال می‌کنیم، اما برای موارد دیگر، درواقع دارایی سفارشی زمینه‌ای جدیدی تعریف می‌کنیم و مقدار را روی آن اعمال می‌کنیم.


:host {
  --n-tab-group-padding: 0;
  --n-tab-list-background: var(--n-color-background);
  --n-tab-list-border: inset 0 -1px 0 0 var(--n-color-border);
  /* ... */
}

.n-tab-group-list {
  box-shadow: var(--n-tab-list-border);
  background-color: var(--n-tab-list-background);
  gap: var(--n-space-s);
  /* ... */
}
ویژگی‌های سفارشی در ریشه سایه مؤلفه تعریف می‌شوند و سپس در سبک‌های مؤلفه استفاده می‌شوند. از ویژگی‌های سفارشی فهرست نشان‌های طراحی نیز استفاده می‌شود.

همچنین برخی‌از مقادیری را که مختص مؤلفه هستند اما در کدهای ما نیستند خلاصه می‌کنیم و آن‌ها را به یک ویژگی سفارشی زمینه‌ای تبدیل می‌کنیم. ویژگی‌های سفارشی که بافتاری برای مؤلفه هستند دو مزیت کلیدی برای ما فراهم می‌کنند. اول اینکه، این یعنی می‌توانیم CSS خود را بیشتر «خشک» کنیم زیرا آن مقدار را می‌توان برای چندین ویژگی در داخل مؤلفه اعمال کرد.


.n-tab-group-list::before {
  /* ... */
  padding-inline-start: var(--n-tab-group-padding);
}

.n-tab-group-list::after {
  /* ... */
  padding-inline-end: var(--n-tab-group-padding);
}
ویژگی سفارشی بافتاری حاشیه داخلی گروه برگه در چندین مکان در کد مؤلفه استفاده می‌شود.

و دوم اینکه، تغییرات حالت و تنوع مؤلفه را واقعاً تمیز می‌کند—فقط ویژگی سفارشی است که باید تغییر کند تا همه آن ویژگی‌ها به‌روز شوند، مثلاً وقتی‌که درحال سبک‌دهی به حالت شناور یا فعال یا در این مورد، یک تنوع هستید.


:host([padding="l"]) {
  --n-tab-group-padding: var(--n-space-l);
}
نوعی از مؤلفه برگه که در آن لایه داخلی بااستفاده از یک به‌روزرسانی ویژگی سفارشی به‌جای چندین به‌روزرسانی تغییر می‌کند.

اما قدرتمندترین مزیت این است که وقتی این ویژگی‌های سفارشی زمینه‌ای را روی یک مؤلفه تعریف می‌کنیم، نوعی CSS API سفارشی برای هریک از مؤلفه‌هایمان ایجاد می‌کنیم که کاربر آن مؤلفه می‌تواند از آن استفاده کند.


<nord-tab-group label="Title">
  <!-- ... -->
</nord-tab-group>

<style>
  nord-tab-group {
    --n-tab-group-padding: var(--n-space-xl);
  }
</style>
استفاده از مؤلفه گروه زبانه‌ها در صفحه و به‌روزرسانی ویژگی سفارشی حاشیه داخلی به اندازه بزرگ‌تر.

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

ما این رویکرد را بسیار قدرتمند می‌دانیم، نه فقط برای ما به‌عنوان سازندگان مؤلفه‌های سیستم طراحی‌مان، بلکه برای تیم توسعه ما نیز وقتی از این مؤلفه‌ها در محصولاتمان استفاده می‌کنند.

استفاده بیشتر از دارایی‌های سفارشی

درحال‌حاضر، ما این ویژگی‌های سفارشی زمینه‌ای را در اسناد خود فاش نمی‌کنیم؛ بااین‌حال، قصد داریم این کار را انجام دهیم تا تیم توسعه گسترده‌تر ما بتواند این ویژگی‌ها را درک کند و از آن‌ها بهره ببرد. مؤلفه‌های ما در npm با یک فایل مانیفست بسته‌بندی شده‌اند که حاوی همه اطلاعات لازم درباره آن‌ها است. سپس وقتی سایت اسناد ما مستقر می‌شود، از فایل مانیفست به‌عنوان داده استفاده می‌کنیم که این کار بااستفاده از Eleventy و ویژگی داده‌های جهانی آن انجام می‌شود. قصد داریم این ویژگی‌های سفارشی زمینه‌ای را در این فایل داده مانیفست بگنجانیم.

زمینه دیگری که می‌خواهیم بهبود دهیم نحوه به‌ارث بردن مقادیر توسط این ویژگی‌های سفارشی زمینه‌ای است. اکنون، برای مثال، اگر می‌خواستید رنگ دو مؤلفه تقسیم‌کننده را تنظیم کنید، باید هر دو مؤلفه را به‌طور خاص با انتخاب‌کننده‌ها هدف قرار دهید یا ویژگی سفارشی را مستقیماً روی عنصر با ویژگی سبک اعمال کنید. این ممکن است خوب به‌نظر برسد، اما اگر توسعه‌دهنده بتواند آن سبک‌ها را در یک عنصر حاوی یا حتی در سطح ریشه تعریف کند، مفیدتر خواهد بود.


<nord-divider></nord-divider>

<section>
  <nord-divider></nord-divider>
   <!-- ... -->
</section>

<style>
  nord-divider {
    --n-divider-color: var(--n-color-status-danger);
  }

  section {
    padding: var(--n-space-s);
    background: var(--n-color-surface-raised);
  }
  
  section nord-divider {
    --n-divider-color: var(--n-color-status-success);
  }
</style>
دو نمونه از مؤلفه تقسیم‌کننده ما که به دو رنگ‌آمیزی متفاوت نیاز دارند. یکی از آن‌ها در بخشی تودرتو است که می‌توانیم از آن برای انتخابگر خاص‌تری استفاده کنیم، اما باید به‌طور خاص جداکننده را هدف قرار دهیم.

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

ویژگی‌های سفارشی خصوصی و عمومی

ویژگی‌های سفارشی خصوصی چیزی است که توسط Lea Verou گردآوری شده است، که یک ویژگی سفارشی «خصوصی» زمینه‌ای در خود مؤلفه است اما روی یک ویژگی سفارشی «عمومی» با یک بازگشت تنظیم شده است.



:host {
  --_n-divider-color: var(--n-divider-color, var(--n-color-border));
  --_n-divider-size: var(--n-divider-size, 1px);
}

.n-divider {
  border-block-start: solid var(--_n-divider-size) var(--_n-divider-color);
  /* ... */
}
CSS مؤلفه وب تقسیم‌کننده با ویژگی‌های سفارشی زمینه‌ای تنظیم‌شده به‌گونه‌ای که CSS داخلی به ویژگی سفارشی خصوصی متکی باشد، که روی ویژگی سفارشی عمومی با بازگشت تنظیم شده است.

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


<nord-divider></nord-divider>

<section>
  <nord-divider></nord-divider>
   <!-- ... -->
</section>

<style>
  nord-divider {
    --n-divider-color: var(--n-color-status-danger);
  }

  section {
    padding: var(--n-space-s);
    background: var(--n-color-surface-raised);
    --n-divider-color: var(--n-color-status-success);
  }
</style>
دوباره دو جداکننده، اما این‌بار می‌توان با افزودن ویژگی سفارشی بافتاری جداکننده به انتخابگر بخش، رنگ جداکننده را تغییر داد. جداکننده آن را به ارث می‌برد و قطعه کد تمیزتر و انعطاف‌پذیرتری تولید می‌کند.

اگرچه ممکن است این بحث مطرح شود که این روش واقعاً «خصوصی» نیست، اما ما همچنان فکر می‌کنیم که این راه‌حل نسبتاً خوبی برای مشکلی است که نگران آن بودیم. وقتی فرصت داشته باشیم، این موضوع را در مؤلفه‌هایمان حل خواهیم کرد تا تیم توسعه ما کنترل بیشتری بر استفاده از مؤلفه داشته باشد و درعین‌حال از حفاظ‌هایی که در جای خود داریم بهره‌مند شود.

امیدوارم این اطلاعات درباره نحوه استفاده ما از «مؤلفه‌های وب» با ویژگی‌های سفارشی CSS برایتان مفید بوده باشد. نظرتان را به ما بگویید و اگر تصمیم گرفتید از هرکدام از این روش‌ها در کار خودتان استفاده کنید، می‌توانید من را در Twitter با نام کاربری @DavidDarnes پیدا کنید. همچنین می‌توانید Nordhealth @NordhealthHQ را در Twitter پیدا کنید، و همچنین بقیه اعضای تیم من را که برای گردآوری این سیستم طراحی و اجرای ویژگی‌های ذکرشده در این سند سخت تلاش کرده‌اند: @Viljamis، @WickyNilliams، و @eric_habich.