نصب و راه‌اندازی

تنها روش رسمی راه‌اندازی سیستم طراحی پرتو — Tailwind v4 با یک import در CSS

یک دستور، یک import

راه‌اندازی پرتو یک مسیر رسمی دارد: نصب پکیج و افزودن یک خط import به فایل CSS اصلی اپلیکیشن. همین import به‌تنهایی استایل کامپوننت‌ها، توکن‌های تم، نگاشت نقش‌ها به utilityهای Tailwind (bg-card، border-border، text-foreground/60، bg-brand، …)، و متغیر dark: را با هم و در ترتیب درست فراهم می‌کند.

پیش‌نیازها:

  • Node.js 18 یا بالاتر
  • React 19 یا بالاتر و React DOM 19 (peer dependency؛ پکیج فقط روی React 19 ساخته و آزموده می‌شود)
  • Tailwind CSS v4 (لازم؛ Tailwind v3 پشتیبانی نمی‌شود — Tailwind v3 و React 18)
  • اگر سایت شما برنامهٔ داده‌ای نیست (سایت بازاریابی، ابزار جدا، نسخهٔ منشعب)، اول دامنهٔ سیستم طراحی را ببینید: هر سطح فقط لایه‌هایی را به کار می‌برد که به کارش می‌آید.
  • Next.js 15 یا بالاتر (React 19) یا هر باندلر مبتنی بر Vite/webpack

گام 1 — نصب

@partodata/ui (و @partodata/mcp-server) در رجیستری خصوصی پکیج‌های گیت‌لبِ شرکت منتشر می‌شود. فقط افراد و CIهایی که دسترسی گرفته‌اند می‌توانند نصبش کنند؛ آدرس رجیستری و دسترسی را تیم سیستم طراحی می‌دهد. این دو خط را در فایل .npmrc پروژه (کنار package.json) بگذارید و به‌جای <registry-address> آدرسی را بنویسید که تیم داده است:

@partodata:registry=https://<registry-address>/
//<registry-address>/:_authToken=${PARTO_REGISTRY_TOKEN}

PARTO_REGISTRY_TOKEN یک متغیر محیطی است؛ هرگز توکن را در مخزن ننویسید و آدرس رجیستری را هم جای عمومی منتشر نکنید.

  • CI محصول: PARTO_REGISTRY_TOKEN=$CI_JOB_TOKEN (گروه محصول در فهرست مجاز توکن کار پروژهٔ سیستم طراحی است).
  • رایانهٔ توسعه‌دهنده: یک توکن دسترسی شخصی با دامنهٔ read_api در PARTO_REGISTRY_TOKEN.
  • ساخت Docker: یک secret از BuildKit فقط برای گام نصب (RUN --mount=type=secret,id=parto_registry_token,env=PARTO_REGISTRY_TOKEN pnpm install --frozen-lockfile)، هرگز ARG یا ENV در لایهٔ ایمیج.
  • بررسی: npm view @partodata/ui version در پوشهٔ محصول نسخهٔ 4.x را چاپ می‌کند.

npx --no parto-ui agents init این دو خط را، با آدرس، اگر نباشند اضافه می‌کند (هرگز مقدار توکن را نه) و npx --no parto-ui check پروژه‌ای را که دامنهٔ @partodata در آن به رجیستری پکیج شرکت نمی‌رود گزارش می‌دهد.

pnpm add @partodata/ui
# پیر-دپندنسی‌ها، نسخهٔ 19 (اگر از قبل نصب نیستند):
pnpm add react@^19 react-dom@^19
# فقط اگر تعویض تم می‌خواهید:
pnpm add next-themes
# فرم‌ها (useForm کنار @partodata/ui/form) — همان بازه‌ای که پکیج استفاده می‌کند، تا هر دو یک نسخه باشند:
pnpm add react-hook-form@^7.85.0
# آیکونی که در Icons نیست:
pnpm add lucide-react

پکیج، وابستگی‌های داخلی خود (recharts، @radix-ui/*، @visx/*، …) را همراه دارد؛ نیازی به سیم‌کشی دستی نیست. اما آنچه کد خود اپ import می‌کند (react-hook-form، lucide-react) وابستگی خود اپ است: با pnpm اپ به وابستگی‌های پکیج دسترسی ندارد.


گام 2 — یک import در CSS

در فایل CSS اصلی اپلیکیشن (مثلاً app/globals.css در Next.js یا src/index.css در Vite)، بعد از @import "tailwindcss"; این خط را اضافه کنید:

/* app/globals.css */
@import 'tailwindcss';
@import '@partodata/ui/tailwind.css';

همین. tailwind.css این چهار چیز را می‌آورد:

بخشنقش
استایل کامپوننت‌هاتمام CSS کامپایل‌شده‌ی کامپوننت‌ها به‌همراه توکن‌های تم؛ utilityهای ازپیش‌ساخته‌ی پرتو در لایه‌ی تودرتوی utilities.parto می‌نشینند
نگاشت نقش‌هاtheme.css — همان نقش‌های معنایی و همان مقدارهای پرتو (رنگ، اندازه و ارتفاع خط متن، گردی گوشه، سایه، انیمیشن) را برای کلاس‌هایی که خودتان می‌نویسید به utility تبدیل می‌کند
کلاس‌های کامپوننت‌هایک @source روی JS پکیج، تا build شما کلاس‌هایی را که کامپوننت‌های پرتو به کار می‌برند هم با همان مقدارهای پرتو بسازد
متغیر dark:@custom-variant dark — به هر دو نشانگر تیرهٔ پرتو ([data-theme='dark'] و .dark) و همچنین به حالت پیش‌فرض (وقتی هیچ نشانگری ست نشده) وصل است

چرا utilityهای شما بر پرتو مقدم‌اند؟

@import 'tailwindcss' utilityهای اپ شما را همان‌جا، پیش از پرتو، می‌نویسد. tailwind.css utilityهای ازپیش‌ساخته‌ی پرتو را در زیرلایه‌ی utilities.parto می‌گذارد و هر utility که مستقیم در لایه‌ی utilities باشد، فارغ از ترتیب، بر آن مقدم است؛ پس hidden lg:flex و px-4 md:px-10 در نقطه‌ی شکست خودشان عوض می‌شوند. کامپوننت‌های پرتو هم همان ظاهر را دارند، چون build شما کلاس‌هایشان را با همان مقدارهای پرتو دوباره می‌سازد. نتیجه‌ی جانبی: اگر در @theme خودتان مقداری را عوض کنید (مثلاً --text-body یا --radius-md)، کامپوننت‌های پرتو هم همان مقدار را می‌گیرند. متن اجزای پرتو روی نقش‌هاست (--text-caption، --text-body، …)، پس --text-xs و --text-sm خود Tailwind فقط به کلاس‌های خود اپ می‌رسد.

استثنا انیمیشن است. پرتو انیمیشن‌ها را با tailwindcss-animate می‌سازد و بسته‌ی tw-animate-css همان نام‌های کلاس را با مقدارهای دیگری تعریف می‌کند. برای همین اعلان‌های انیمیشن پرتو (animation*، --tw-enter-*، --tw-exit-*) مستقیم در لایه‌ی utilities و بعد از utilityهای شما می‌مانند و مثل قبل برنده‌اند؛ پس کامپوننت‌های پرتو، هر بسته‌ی انیمیشنی که import کنید، همان حرکتِ مستندات را دارند.

importهای جداگانه در Tailwind v4

اگر styles.css و theme.css را جدا import کنید، utilityهای ازپیش‌ساخته‌ی پرتو در همان لایه‌ی شما و بعد از آن‌ها می‌آیند و نسخه‌ی پایه‌ی هر کلاسی که پرتو هم به کار می‌برد (.hidden، .px-4، …) کلاس واکنش‌گرا شما را در همه‌ی عرض‌ها باطل می‌کند. در Tailwind v4 همان دو خط بالا تنها راه است؛ styles.css ورودی مصرف‌کننده نیست و سطحی که فقط لایهٔ پایه را می‌گیرد، tokens.css را بار می‌کند (پایین‌تر).


گام 3 — ویژگی‌های <html> (پیش‌فرض تیره)

پرتو تیره-اول است: تم پایه در :root تیره است، و روشن یک opt-in صریح زیر [data-theme='light'] / .light است. راه‌اندازی پیشنهادی برای یک اپ تک‌تم، تثبیت صریح نشانگرهای تیره روی <html> است تا SSR بدون پرش رنگ رندر شود:

// app/layout.tsx (Next.js App Router)
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="fa" dir="rtl" className="dark" data-theme="dark" suppressHydrationWarning>
      <body>{children}</body>
    </html>
  )
}
ویژگیچرا لازم است
lang="fa"رقم فارسی را روشن می‌کند و برای screen readerها و موتورهای جستجو لازم است (زیر جدول)
dir="rtl"برای کارکرد صحیح CSS Logical Properties الزامی است
className="dark" + data-theme="dark"هر دو نشانگر تیره؛ مصرف‌کننده‌ی تیره‌ی صریح دقیقاً مثل قبل رندر می‌شود
suppressHydrationWarningفقط اگر تم را به‌صورت داینامیک عوض می‌کنید (گام 6) لازم است

lang="fa" فقط برای موتور جستجو نیست

از 4.0 رقم فارسی از قابلیت ss01 فونت می‌آید و این قابلیت فقط زیر lang="fa" روشن است (fa-IR و هر fa-… هم حساب می‌شود؛ fa_IR، per و fas نه). صفحه‌ای که lang ندارد یا زبان دیگری دارد، رقم لاتین نشان می‌دهد. جزئیات: اعداد فارسی.

اگر هیچ نشانگری نگذارید

یک اپ که هیچ تمی روی &lt;html&gt; نگذارد، حالا تیره رندر می‌شود (پیش‌فرض :root). برای روشن، صریحاً opt-in کنید: &lt;html className="light" data-theme="light"&gt;.


گام 4 — فونت (یکان بخ)

فونت یکان بخ Variable داخل خود پکیج توزیع می‌شود (فایل woff2 در dist/assets/fonts/ و یک @font-face که با url() نسبی به آن اشاره می‌کند). با همان import گام 2، فونت به‌صورت خودکار بارگذاری می‌شود — نیازی به self-host کردن ندارید و نباید فونت جداگانه اضافه کنید.

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

اگر باندلر شما url() داخل CSS پکیج‌های node_modules را resolve نمی‌کند (نادر — برخی لودرهای CSS سخت‌گیر)، فایل YekanBakh-VF.woff2 را از node_modules/@partodata/ui/dist/assets/fonts/ به فولدر public خود کپی کرده و @font-face خودتان را تعریف کنید. این یک fallback عیب‌یابی است، نه یک گام از نصب.


گام 5 — Next.js: transpilePackages

فقط برای Next.js: پکیج باید در transpilePackages باشد، وگرنه build با SyntaxError: Cannot use import statement شکست می‌خورد.

// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  transpilePackages: ['@partodata/ui'],
}

export default nextConfig

این تنظیم مخصوص Next.js است؛ برای Vite/CRA لازم نیست.


گام 6 — تعویض تم با next-themes (اختیاری)

فقط وقتی محصول کلید روشن/تیره می‌خواهد؛ محصولی که نمی‌خواهد، همان نشانگرهای ثابت گام 3 را دارد. برای کلید، جفت خود پکیج را به کار ببرید، ThemeProvider و ThemeToggle از @partodata/ui/theme-toggle (به next-themes نیاز دارند: pnpm add next-themes)، و next-themes را خودتان سیم‌کشی نکنید:

// app/layout.tsx — ThemeProvider دور محتوای body؛ نشانگرها روی <html> می‌مانند
import { ThemeProvider } from '@partodata/ui/theme-toggle'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="fa" dir="rtl" className="dark" data-theme="dark" suppressHydrationWarning>
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  )
}

ThemeProvider پیش‌فرض تیره دارد و کلاس و data-theme را با هم و پیش از اولین رسم می‌نویسد، پس دو نشانگر هیچ‌وقت از هم جدا نمی‌شوند. کلید تم را خود قاب (ProductFrame) در یک جای ثابت می‌گذارد (themeToggle="header" پیش‌فرض، یا "user-menu")؛ ThemeToggle را در actions نگذارید.


گام 7 — تأیید نصب

دو بررسی ساده که ثابت می‌کند فونت، توکن‌ها، تم، و اسکن Tailwind همگی کار می‌کنند:

1. بررسی فونت — در کنسول مرورگر یا یک کامپوننت کلاینت:

'use client'

import { useEffect } from 'react'

export function FontCheck() {
  useEffect(() => {
    const family = getComputedStyle(document.body).fontFamily
    // باید شامل «Yekan Bakh» باشد
    console.log('font-family:', family)
  }, [])
  return null
}

2. تست دودی سطوح — این بلوک را در هر صفحه رندر کنید:

<div className="bg-surface-100 text-foreground border border-default rounded-md p-4">
  اگر این کادر روی یک سطح تیره با حاشیه‌ی محو دیده می‌شود، توکن‌ها و تم درست بارگذاری شده‌اند.
</div>

اگر کادر بدون استایل (سفید/بی‌حاشیه) دیده شد، معمولاً import گام 2 جا افتاده است.


گام 8 — فعال‌سازی گیت‌های مصرف

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

پلاگین ESLint

پیکربندی روی همهٔ فایل‌های .ts/.tsx اپ (با app/) و با parser تایپ‌اسکریپت اجرا می‌شود (pnpm add -D eslint typescript-eslint). برنامهٔ داده‌ای parto.configs.recommended را می‌گیرد و سطحی با لایه‌های 1 و 2 (دامنه) parto.configs.components را:

// eslint.config.mjs
import tseslint from 'typescript-eslint'
import parto from '@partodata/ui/eslint-plugin'

export default [
  { ignores: ['.next/**', 'node_modules/**', 'next-env.d.ts'] },
  // بدون parser تایپ‌اسکریپت، ESLint 9 فایل‌های JSX/TS را تحلیل نمی‌کند و قاعده‌ها روی کد اجرا نمی‌شوند.
  { files: ['**/*.{ts,tsx}'], languageOptions: { parser: tseslint.parser }, ...parto.configs.recommended },
]

با eslint . --max-warnings 0 اجرا کنید.

36 قانون flat-config (9 روی error و 19 روی warn): بدون رنگ hardcode/پالت، بدون متن خارج از مقیاس، بدون CSS فیزیکی، الزام data-slot، جفت‌رنگ WCAG، بدون متن بدنه‌ی محو، و بدون letter-spacing روی فارسی.

اسکنر parto-design-lint

یک CLI بدون وابستگی که رشته‌های className و CSS خام را برای نقض‌هایی که قوانین AST نمی‌بینند اسکن می‌کند (مقادیر دلخواه text-[…]/rounded-[…px]، literalهای hex/rgb()/hsl()، کلاس‌های پالت خام، utilityهای جهت فیزیکی):

// package.json
{
  "scripts": {
    "design-lint": "parto-design-lint src",
    "build": "parto-design-lint src && next build"
  }
}

کدهای خروج: 0 تمیز · 1 نقض · 2 صفر فایل اسکن‌شده (مسیر اشتباه هرگز به‌جای «تمیز» جا نمی‌زند). این جایگزین اسکریپت‌های design-lint.sh است که هر اپ جداگانه نگه می‌داشت.

قرارداد مصرف

این دو گیت، پنج محدودیت ترکیب‌بندی پرتو را اجرا می‌کنند. برای شرح کامل قرارداد و مرور سه‌پرسشی، صفحه‌ی قرارداد مصرف را ببینید.


استفاده اولیه

قاب فقط مال برنامهٔ داده‌ای است (ردیف دوم جدول دامنه)؛ هر سطح دیگر قاب و قالب‌های صفحه را کنار می‌گذارد. برنامهٔ داده‌ای یک قاب دارد، ProductFrame. اگر اپ مسیری بیرون از قاب دارد (ورود، منبع PDF، اسلاید)، layout ریشه فقط <html>، <body>، providerها و <Toaster /> را نگه می‌دارد و app/(app)/layout.tsx قاب را می‌گذارد، <Frame>{children}</Frame>؛ وگرنه قاب در layout ریشه است (نسخهٔ کامل App Router، frame.tsx و layout.tsx، در همان صفحه و در قالب شروع). هر صفحه داخل آن یک قالب صفحه از @partodata/ui/templates است — فهرست، جزئیات، فرم، تنظیمات، داشبورد یا صفحهٔ کمکی (انتخاب قالب صفحه). صفحه فقط جایگاه‌های قالب را پر می‌کند و عرض، عنوان، فاصله‌ها، جای اقدام‌ها و حالت‌ها را قالب تعیین می‌کند:

'use client'

import Link from 'next/link'
import { Button, DataTable, type DataTableColumn } from '@partodata/ui'
import { Icons } from '@partodata/ui/icons'
import { ListPage } from '@partodata/ui/templates'

type Source = { id: string; name: string }

const columns: DataTableColumn<Source>[] = [{ id: 'name', header: 'منبع', cell: (row) => row.name }]
const sources: Source[] = [{ id: '1', name: 'صفحهٔ رسمی برند در اینستاگرام' }]

// app/sources/page.tsx
export default function Page() {
  return (
    <ListPage
      title="منبع‌ها"
      primaryAction={
        <Button asChild iconStart={<Icons.plus />}>
          <Link href="/sources/new">افزودن منبع</Link>
        </Button>
      }
      // Rows written in the code never load; rows from an API: state={pageState({ data: result?.items, … })}
      state={{ status: 'ready' }}
    >
      <DataTable columns={columns} data={sources} />
    </ListPage>
  )
}

داده‌ای که از API می‌آید حالت بارگذاری، خطا و خالی هم دارد؛ آن را با state={pageState({ data: result?.items, isLoading, error, onRetry })} به همان قالب بدهید — data خودِ فهرست است، نه شیء صفحه‌ای که API برمی‌گرداند (PageState).

یک قاعدهٔ import برای کد اپ: همه‌چیز از @partodata/ui، و فقط ورودی‌هایی که در بارل نیستند از subpath خودشان — قالب‌های صفحه از @partodata/ui/templates، قاب برنامه ProductFrame از @partodata/ui/product-frame، PageToolbar از @partodata/ui/page-toolbar، Form* از @partodata/ui/form، ThemeToggle از @partodata/ui/theme-toggle و Icons از @partodata/ui/icons. subpath دیگری را حدس نزنید: نام فایل منبع subpath نیست. بارل یک ماژول است و صفحه‌ای که چیزی از آن import کند هزینهٔ همه‌اش را داده است؛ subpathهای تک‌کامپوننتی (فهرست‌شان در exports فایل package.json) فقط در باندلی به کار می‌آیند که اصلاً بارل را import نمی‌کند، مثل کتابخانه یا ویجتی که روی سیستم طراحی ساخته شده است. چنین باندلی هر خانوادهٔ مرکب را از یک ورودی وارد می‌کند (مثلاً همهٔ قطعه‌های Page* از @partodata/ui/page) و بارل و subpath را در یک خانواده مخلوط نمی‌کند: context خانواده‌ها در هر ورودی نسخهٔ جدای خودش است (مثلاً NavRail* و useNavRail). چند context عمداً بین همهٔ ورودی‌ها مشترک است تا لایهٔ صفحه از هر ورودی درست کار کند: اندازهٔ کنترل (ControlSizeProvider)، قاب (ProductFrame)، کنترل تم (ThemeProvider برای ProductFrame)، FilterBar و PageContainer. از 4.2 هر کامپوننتی که به context ورودی دیگری وابسته نیست subpath خودش را دارد (مثلاً @partodata/ui/command-palette، @partodata/ui/period-selector و @partodata/ui/word-cloud).


Tailwind v3 و React 18: فقط لایهٔ پایه

Tailwind v3 پشتیبانی نمی‌شود. کلاس‌های کامپوننت‌های پرتو نحو Tailwind v4 دارند (h-(--layout-header-height)، outline-hidden، bg-brand/10 روی توکن‌ها، container query)، پس preset نسخهٔ 3 نمی‌تواند آن‌ها را بسازد؛ و stylesheet ازپیش‌ساختهٔ پرتو (styles.css) یک build کامل Tailwind v4 است، با reset پایه، کلاس‌های utility و ثبت سراسری @property:

  • اگر از JavaScript import شود (import '@partodata/ui/styles.css')، build نسخهٔ 3 متوقف می‌شود: «@layer base is used but no matching @tailwind base directive is present».
  • اگر در CSS با @import بیاید یا جدا با <link> روی همان صفحه بار شود، کلاس‌های خود اپ را عوض می‌کند: گرادیان نسخهٔ 3 شفاف می‌شود (ثبت @property --tw-gradient-from)، rotate-180 و -translate-y-1/2 و scale-110 دو بار اعمال می‌شوند (نسخهٔ 4 با rotate/translate/scale جابه‌جا می‌کند و نسخهٔ 3 با transform)، reset پایه پس‌زمینهٔ ورودی‌ها، اندازهٔ تیترها و نشانهٔ فهرست‌ها را برمی‌دارد، و صفحه تیره می‌شود.

@partodata/ui/tailwind.config پیکربندی build خود سیستم طراحی است، نه preset برای اپ.

React 18 هم پشتیبانی نمی‌شود: پکیج react >=19 می‌خواهد و فقط روی React 19 ساخته و آزموده می‌شود. روی React 18 کامپوننت‌هایی که تابع ساده‌اند (DropdownMenu*، Sheet*، ContextMenu*، Table*، Avatar، Label، Toggle و دیگران) ref داده‌شده به خود را دور می‌اندازند (React هشدار «Function components cannot be given refs» می‌دهد)، و GatedActionی که پنجره‌ای را باز می‌کند ref آن را گم می‌کند: پس از بسته شدن پنجره فوکوس به آن برنمی‌گردد. اپ React 18 نسخهٔ 4.x را نصب نمی‌کند: npm با خطای ERESOLVE (پیر react >=19) متوقف می‌شود. آن را هرگز با --legacy-peer-deps، --force یا override نصب نکنید.

راه درست ارتقای سکوست: npx @tailwindcss/upgrade برای رفتن به Tailwind v4 (راهنمای مهاجرت)، و react و react-dom نسخهٔ 19. اپی که از قبل کامپوننت‌های پرتو 3.x را روی Tailwind v3 یا React 18 به کار می‌برد، تا ارتقا روی 3.x می‌ماند. تا آن زمان، یا برای سطحی که اصلاً کامپوننت‌ها را لازم ندارد (سایت ایستا، ابزار تک‌فایلی، نسخهٔ منشعب)، لایهٔ پایه (دامنهٔ سیستم طراحی) را از tokens.css بگیرید: توکن‌های هر دو تم، فونت یکان بخ و قاعدهٔ رقم‌ها؛ بدون reset، بدون utility، بدون @property، بدون @layer و همهٔ قاعده‌ها با specificity صفر (:where). این سطح پکیج را نصب نمی‌کند (React 18 نمی‌تواند، و اپی که روی 3.x است در node_modules نسخهٔ 3.x دارد که tokens.css ندارد): فایل‌های یک نسخهٔ دقیق را برمی‌دارد.

# یک نسخهٔ دقیق که آگاهانه عوض می‌شود؛ پکیج نصب نمی‌شود
npm pack @partodata/ui@4.0.0
tar -xzf partodata-ui-4.0.0.tgz package/dist/tokens.css package/dist/assets
mkdir -p public/vendor/partodata-ui
cp -r package/dist/tokens.css package/dist/assets public/vendor/partodata-ui/
rm -rf package partodata-ui-4.0.0.tgz
<!-- index.html: نشانگر تم روی <html>، و پیوند در <head> -->
<html lang="fa" dir="rtl" data-theme="light">
  <head>
    <link rel="stylesheet" href="/vendor/partodata-ui/tokens.css" />
  </head>
</html>
  • پوشهٔ assets باید کنار tokens.css بماند: فونت با url() نسبی بار می‌شود و اگر جای دیگری باشد، فونت و رقم‌های فارسی بی‌هیچ خطایی از دست می‌روند.
  • نسخهٔ 4.0 فقط در رجیستری داخلی منتشر می‌شود و روی CDN عمومی نیست؛ صفحه‌ای که پوشهٔ فایل ثابت ندارد (فایل HTML تک، گزارشی که سرور می‌سازد) tokens.css و پوشهٔ assets را از بستهٔ نصب‌شده کنار خودش می‌گذارد یا از سرور فایل ثابت داخلی همان سکو پیوند می‌دهد — با نسخهٔ دقیق، هرگز بازه‌ای مثل @4.
  • CSS خود سایت توکن‌ها را می‌خواند و فونت را می‌گذارد، مثلاً body { font-family: 'Yekan Bakh', sans-serif; color: var(--foreground-default); background: var(--background-default); }.
  • نشانگر تم (data-theme="light" یا "dark"، یا کلاس light/dark) تم توکن‌ها را تعیین می‌کند. فقط data-theme color-scheme را هم می‌گذارد؛ کلاس تنها مقدار توکن‌ها را عوض می‌کند، پس اپ Tailwind v3 که تم را با کلاس dark عوض می‌کند، color-scheme خودش را نگه می‌دارد. بدون نشانگر، توکن‌ها مقدار تیره دارند و color-scheme صفحه عوض نمی‌شود.
  • متغیری که سایت با نام یکی از توکن‌ها تعریف کرده (--border-strong، --spacing-sm، --z-modal، --chart-1 و …): اگر بیرون از @layer باشد مقدار سایت می‌ماند، اما اگر داخل @layer باشد (مثل @theme در Tailwind v4)، tokens.css بر آن غلبه می‌کند؛ متغیر سایت را تغییر نام دهید. جز این‌ها و رقم‌ها، تا CSS سایت توکنی را نخواند چیزی در صفحه عوض نمی‌شود.
  • لایهٔ پایه مقیاس تایپ ندارد (نام --text-* در آن نیست). سایت اندازه‌ها را از مقدار نقش‌ها (4.0) می‌گذارد، ارتفاع خط در پرانتز: micro 10px، mini 11px، caption 12px (18px)، label 14px (22px)، body 14px (22px)، subheading 14px (22px، وزن 600)، heading 18px (28px، 600)، display 22px (32px، 600).
  • ایمیل tokens.css را بار نمی‌کند (کلاینت‌های ایمیل <link>، var() و color-mix() را حذف می‌کنند): رنگ‌های تم روشن را به‌صورت hex و درون‌خطی می‌نویسد، از palette-light.json همان نسخه (package/dist/palette-light.json را هم از همان tarball بیرون بیاورید، یا همان مسیر را در CDN بخوانید). هر رنگ مات است، همان‌طور که روی صفحهٔ روشن دیده می‌شود.
  • رقم‌ها از lang پیروی می‌کنند: در متن یکان بخ زیر lang="fa" رقم لاتین فارسی کشیده می‌شود؛ عنصری با lang دیگر (<code lang="en">185.12.3.4</code>، صفحهٔ عربی) رقم خودش را نگه می‌دارد، و فونت دیگری روی صفحه دست نمی‌خورد.

کامپوننت‌ها با ارتقای سکو می‌آیند.


مشکلات رایج و راه‌حل

کامپوننت‌ها بدون استایل نمایش داده می‌شوند

علت: import گام 2 جا افتاده است.

/* ✅ درست — در app/globals.css */
@import 'tailwindcss';
@import '@partodata/ui/tailwind.css';

کلاس واکنش‌گرا اعمال نمی‌شود (hidden lg:flex همیشه پنهان می‌ماند)

علت: styles.css و theme.css جدا import شده‌اند، یا نسخه‌ی نصب‌شده قدیمی‌تر از اصلاحی است که utilityهای پرتو را به لایه‌ی utilities.parto برد (یادداشت انتشار را ببینید). در هر دو حالت نسخه‌ی پایه‌ی پرتو (.hidden) بعد از کلاس واکنش‌گرا شما می‌آید و برنده می‌شود.

/* ✅ درست — در app/globals.css */
@import 'tailwindcss';
@import '@partodata/ui/tailwind.css';

خطای build: SyntaxError: Cannot use import statement

علت: پکیج در transpilePackages نیست (فقط Next.js).

// next.config.mjs ✅
const nextConfig = { transpilePackages: ['@partodata/ui'] }

فونت یکان بخ اعمال نمی‌شود

علت: باندلر url() فونت را resolve نکرده. به گام 4 و راه‌حل خودمیزبانی مراجعه کنید.

خطای build: «@layer base is used but no matching @tailwind base directive is present»

علت: اپ روی Tailwind v3 است و styles.css را import کرده است. Tailwind v3 پشتیبانی نمی‌شود؛ سکو را ارتقا دهید یا لایهٔ پایه را از tokens.css بگیرید (Tailwind v3 و React 18).

گرادیان‌های اپ شفاف شده‌اند یا آیکون‌ها جابه‌جا شده‌اند

علت: styles.css با <link> کنار CSS یک اپ Tailwind v3 (یا سایتی بدون Tailwind) بار شده است؛ این فایل یک build کامل Tailwind v4 است و کلاس‌ها و reset خود را به کل صفحه می‌دهد. پیوند را به tokens.css عوض کنید (Tailwind v3 و React 18).

layout معکوس است (RTL کار نمی‌کند)

علت: dir="rtl" روی <html> نیست. CSS Logical Properties به این attribute نیاز دارند.

تم وسط تعویض با خودش اختلاف دارد

علت: next-themes دستی سیم‌کشی شده و فقط یک نشانگر ست می‌شود. ThemeProvider را از @partodata/ui/theme-toggle بگیرید (گام 6): کلاس و data-theme را با هم می‌نویسد.