پرتوپرتو

آیکون‌ها

اصول طراحی و نحوه استفاده از آیکون‌ها در پرتو — با گالری تعاملی

اصول

  1. جفت شده: آیکون‌ها باید همراه با متن باشند، زیرا به تنهایی اغلب واضح نیستند.
  2. واضح: آیکون‌ها باید در اندازه‌های کوچک خوانا و بدون تزئین باشند. بگذارید متن کار اصلی را انجام دهد.
  3. یکنواخت: از همان آیکون‌ها برای اقدامات مشابه در سراسر پرتو استفاده کنید. این باعث می‌شود اپلیکیشن استفاده آسان‌تری داشته باشد.

قوانین اجباری

قوانین زیر چکیدهٔ لازم‌الاجرای این صفحه هستند؛ جزئیات و استدلال کامل هر قانون در سایر بخش‌های همین صفحه آمده است.

آیکون همیشه همراه متن — هرگز آیکون تنها

// ✅ درست — آیکون در کنار متن؛ معنا برای همه کاربران روشن است
<Button>
  <Icons.search className="size-4" />
  جستجو
</Button>

// ❌ غلط — آیکون تنها؛ کاربر باید معنای آن را حدس بزند
<Button>
  <Icons.search className="size-4" />
</Button>

آیکون‌ها به‌تنهایی اغلب واضح نیستند؛ انتقال معنا بر عهده متن است و آیکون فقط آن را تقویت می‌کند.

اندازه فقط با کلاس‌های size-* — پراپ عددی size مختص آیکون‌های Lucide است

// ✅ درست — کلاس size-* روی همه آیکون‌ها (Lucide و سفارشی) یکسان عمل می‌کند
<Icons.parto className="size-6" />

// ❌ غلط — آیکون سفارشی inline-SVG پراپ size را نمی‌شناسد و بدون اندازه‌گذاری رندر می‌شود
<Icons.parto size={24} />

پراپ‌های عددی size و strokeWidth مختص آیکون‌های Lucide هستند؛ آیکون‌های سفارشی (مانند parto و logo) فقط React.SVGProps می‌پذیرند و TypeScript هم این اشتباه را نمی‌گیرد.

آیکون‌های جهت‌دار در RTL باید چرخانده شوند

// ✅ درست — فلش در چیدمان راست‌به‌چپ به سمت درست اشاره می‌کند
<Icons.arrowRight className="size-4 rtl:rotate-180" />

// ❌ غلط — بدون rtl:rotate-180 فلش «بعدی» به سمت اشتباه اشاره می‌کند
<Icons.arrowRight className="size-4" />

در رابط فارسی جهت پیشروی معکوس است؛ فلش‌ها و شِورون‌های بدون چرخش کاربر را به مسیر اشتباه هدایت می‌کنند (آیکون‌های غیرجهت‌دار مانند search و settings نیازی به چرخش ندارند).

رنگ فقط از توکن‌های سمانتیک یا ارث‌بری — هرگز رنگ hardcode

// ✅ درست — رنگ از توکن سمانتیک؛ در هر دو تم درست رندر می‌شود
<Icons.check className="size-4 text-brand-default" />

// ❌ غلط — رنگ hardcode؛ با تغییر تم به‌روز نمی‌شود
<Icons.check className="size-4" style={{ color: '#16a34a' }} />

آیکون‌ها از طریق currentColor رنگ متن والد را ارث می‌برند؛ رنگ hardcode این زنجیره را قطع می‌کند و خوانایی را در تم مقابل از بین می‌برد.

آیکون‌های خارج از آبجکت Icons را مستقیم از lucide-react بگیرید

// ✅ درست — import نام‌برده؛ فقط همان آیکون وارد bundle می‌شود
import { BarChart3 } from 'lucide-react'
;<BarChart3 className="size-4" />

// ❌ غلط — barChart3 در آبجکت Icons وجود ندارد؛ خروجی undefined و خطای رندر است
import { Icons } from '@partodata/ui'
;<Icons.barChart3 className="size-4" />

آبجکت Icons عمداً فقط آیکون‌های منتخب را دارد (به دلیل tree-shaking) و چون تایپ آن Record<string, …> است، TypeScript دسترسی به کلید ناموجود را خطا نمی‌گیرد — برای بقیه 1714 آیکون همیشه import مستقیم بزنید.


گالری آیکون‌ها

گالری شامل همه آیکون‌های lucide-react به همراه آیکون‌های curated DS است — روی هر آیکون کلیک کنید تا کد استفاده آن کپی شود. آیکون‌های دارای نشان DS از طریق آبجکت Icons در دسترس‌اند؛ بقیه را با import مستقیم از lucide-react استفاده کنید.

۵۲ آیکون curated در آبجکت Icons + ۱٬۷۱۴ آیکون از lucide-react — جمعاً ۱٬۷۶۶ آیکون.

روی هر آیکون کلیک کنید تا کد استفاده کپی شود. آیکون‌های دارای نشان DS از طریق Icons.* در دسترس‌اند، بقیه را با import { X } from 'lucide-react' استفاده کنید.

آیکون‌های DS (curated)

برند و لوگو

ناوبری و جهت

عملیات

بازخورد و وضعیت

محتوا و رسانه

متن و ویرایش

سایر

همه آیکون‌های Lucide

۲۴۰ از ۱٬۷۱۴

دو روش استفاده

۱. آیکون‌های DS (توصیه‌شده برای آیکون‌های پرکاربرد)

DS یک آبجکت Icons export می‌کند که شامل آیکون‌های پرکاربرد Lucide و لوگوهای سفارشی است:

import { Icons } from '@partodata/ui'

<Icons.search className="size-4" />
<Icons.settings className="size-4" />
<Icons.parto className="size-6" />

۲. Import مستقیم از Lucide (برای آیکون‌های خاص)

برای هر یک از 1714 آیکون lucide که در آبجکت Icons نیستند، import مستقیم بزنید:

import { BarChart3, Zap, Activity } from 'lucide-react'
;<BarChart3 className="size-4" />

نکته: lucide-react یک peer dependency ضمنی DS است — اگر DS نصب باشد، lucide-react هم در دسترس است.

چرا همه آیکون‌ها در Icons نیستند؟ آبجکت Icons به دلیل ماهیت dynamic property access قابل tree-shake نیست؛ اگر همه 1714 آیکون داخلش بودند، حتی استفاده از یک <Icons.search /> کل آن‌ها را وارد bundle می‌کرد. import مستقیم از lucide-react تنها همان آیکون مصرف‌شده را وارد bundle می‌کند.


اندازه‌بندی

از سیستم size-* Tailwind استفاده کنید:

سایزکلاسکاربرد
12pxsize-3داخل badge، متن خیلی کوچک
16pxsize-4پیش‌فرض، داخل دکمه، کنار متن
18pxsize-[18px]آیکون‌های منوی سایدبار
20pxsize-5آیکون‌های بزرگ‌تر
24pxsize-6آیکون‌های hero، لوگو

RTL

آیکون‌های جهت‌دار (فلش‌ها، شِورون‌ها) باید در RTL چرخانده شوند:

<Icons.arrowRight className="size-4 rtl:rotate-180" />
<Icons.chevronRight className="size-4 rtl:rotate-180" />

آیکون‌های غیرجهت‌دار (search, settings, home) نیاز به چرخش ندارند.


رنگ‌آمیزی

آیکون‌ها رنگ متن والد را ارث می‌برند. برای تنظیم مستقیم از کلاس‌های رنگ‌های متن استفاده کنید:

<Icons.check className="size-4 text-brand-default" />
<Icons.alertCircle className="size-4 text-destructive-default" />
<Icons.info className="size-4 text-foreground-muted" />

آیکون‌ها را با text-destructive برای اقدامات مخرب رنگ‌آمیزی نکنید. باید یک دیالوگ تأیید بلافاصله بعد از آن وجود داشته باشد که می‌تواند استایل مخرب را مدیریت کند.


آیکون‌های سفارشی

وقتی Lucide آیکون مورد نیاز شما را ندارد، آیکون‌های سفارشی ایجاد و استفاده کنید.

استفاده

import { Icons } from '@partodata/ui'

// آیکون سفارشی (inline SVG) — اندازه فقط با کلاس‌های size-*
<Icons.parto className="size-4 text-brand-default" />

// آیکون Lucide — علاوه بر className، پراپ‌های عددی size و strokeWidth را هم می‌پذیرد
<Icons.search size={16} strokeWidth={1.5} />

تفاوت مهم در اندازه‌بندی: پراپ‌های عددی size و strokeWidth مختص آیکون‌های Lucide هستند (پیش‌فرض Lucide: size={24} و strokeWidth={2}). آیکون‌های سفارشیِ inline-SVG (مانند parto، logo و gitHub) پراپ size را نمی‌شناسند — اندازه آن‌ها را همیشه با کلاس‌های size-* تعیین کنید. رنگ در هر دو حالت از طریق currentColor از متن والد ارث برده می‌شود.

افزودن آیکون‌های سفارشی جدید

همه آیکون‌های DS در یک فایل واحد و دست‌نویس تعریف شده‌اند: packages/ui/src/icons.tsx. این فایل آبجکت Icons را export می‌کند و افزودن آیکون جدید یعنی ویرایش دستی همین فایل — هیچ مرحله build یا codegen جداگانه‌ای وجود ندارد.

  1. ویرایش packages/ui/src/icons.tsx: یک کلید جدید با نام camelCase به آبجکت Icons اضافه کنید. دو حالت ممکن است:

    • re-export یک آیکون Lucide — آیکون را در بالای فایل از lucide-react import کنید و مستقیماً به کلید نسبت دهید:

      // ۱) در بالای فایل icons.tsx
      import { Bookmark } from 'lucide-react'
      
      // ۲) داخل آبجکت Icons
      bookmark: Bookmark,
    • کامپوننت SVG سفارشی — یک function component بنویسید که React.SVGProps<SVGSVGElement> می‌گیرد و آن را با {...props} روی عنصر ریشه <svg> پخش (spread) می‌کند؛ دقیقاً به همان شکل ورودی‌های سفارشی موجود در فایل (parto، logo، gitHub):

      // داخل آبجکت Icons در packages/ui/src/icons.tsx
      myNewIcon: (props: React.SVGProps<SVGSVGElement>) => (
        <svg
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          strokeWidth="2"
          strokeLinecap="round"
          strokeLinejoin="round"
          {...props}
        >
          <path d="M6 7C6 4.2 8.2 2 11 2H13C15.8 2 18 4.2 18 7" />
        </svg>
      ),

    الزامات SVG سفارشی:

    • viewBox="0 0 24 24" با محتوای آیکون حدود 18x18px درون فریم 24x24
    • stroke="currentColor" برای آیکون‌های خطی یا fill="currentColor" برای آیکون‌های توپر (بدون رنگ‌های hardcoded)
    • {...props} روی عنصر ریشه <svg> spread شود تا className و سایر پراپ‌های SVG از سمت مصرف‌کننده اعمال شوند
    • عناصر غیرضروری (<clipPath>، <defs>، wrapper های <g>) حذف شده باشند و ساختار تا حد امکان ساده و فقط شامل عناصر <path> باشد
    • نام attribute ها در JSX باید camelCase باشند (strokeWidth نه stroke-width)
  2. استفاده: آیکون بلافاصله و بدون هیچ مرحله اضافه‌ای در دسترس است:

    import { Icons } from '@partodata/ui'
    ;<Icons.myNewIcon className="size-4" />

مثال

// ❌ بد — رنگ‌های hardcoded، بدون spread کردن props
badIcon: () => (
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
    <rect width="24" height="24" fill="#1E1E1E" />
    <path d="M12 2L2 7l10 5 10-5-10-5z" fill="#404040" />
  </svg>
),

// ✅ خوب — currentColor، ساختار تمیز و spread کردن props
goodIcon: (props: React.SVGProps<SVGSVGElement>) => (
  <svg
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth="1.5"
    strokeLinecap="round"
    strokeLinejoin="round"
    {...props}
  >
    <path d="M6 7C6 4.2 8.2 2 11 2H13C15.8 2 18 4.2 18 7" />
  </svg>
),

عیب‌یابی

ویژگی‌های ارائه (stroke، strokeWidth، fill) را روی عنصر ریشه <svg> و قبل از {...props} قرار دهید تا مصرف‌کننده در صورت نیاز بتواند آن‌ها را override کند. اگر این ویژگی‌ها روی <path> های داخلی hardcode شوند، از بیرون کامپوننت قابل تغییر نیستند و رنگ یا ضخامت خط آیکون قفل می‌شود.