نصب و راهاندازی
تنها روش رسمی راهاندازی دیزاین سیستم پرتو — 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)
- Tailwind CSS v4 (مسیر پیشنهادی؛ برای v3 به بخش سازگاری با Tailwind v3 مراجعه کنید)
- Next.js 14+ یا هر باندلر مبتنی بر Vite/webpack
گام ۱ — نصب
pnpm add @partodata/ui
# پیر-دپندنسیها (اگر از قبل نصب نیستند):
pnpm add react react-dom
# فقط اگر تعویض تم میخواهید:
pnpm add next-themesپکیج، وابستگیهای داخلی خود (recharts، @radix-ui/*، @visx/*، …) را همراه دارد؛ نیازی به سیمکشی دستی نیست.
گام ۲ — یک 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 کامپایلشدهی کامپوننتها بههمراه توکنهای تم (styles.css) |
| نگاشت نقشها | theme.css — همان نقشهای معنایی را برای کلاسهایی که خودتان مینویسید به utility تبدیل میکند |
متغیر dark: | @custom-variant dark که به هر دو نشانگر تیرهی پرتو ([data-theme='dark'] و .dark) وصل است |
چرا یک import و نه چند تا؟
ترتیب import اهمیت دارد و tailwind.css آن را تضمین میکند و متغیر dark: را هم اضافه میکند. importهای جداگانه
(styles.css + theme.css) فقط برای مصرف پیشرفته و کنترل دقیقتر لازم است — برای اکثر اپها همان یک خط کافی است.
گام ۳ — ویژگیهای <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 | فقط اگر تم را بهصورت داینامیک عوض میکنید (گام ۶) لازم است |
اگر هیچ نشانگری نگذارید
یک اپ که هیچ تمی روی <html> نگذارد، حالا تیره رندر میشود (پیشفرض :root). برای روشن، صریحاً opt-in
کنید: <html className="light" data-theme="light">.
گام ۴ — فونت (یکان بخ)
فونت یکان بخ Variable داخل خود پکیج توزیع میشود (فایل woff2 در dist/assets/fonts/ و یک @font-face که با url() نسبی به آن اشاره میکند). با همان import گام ۲، فونت بهصورت خودکار بارگذاری میشود — نیازی به self-host کردن ندارید و نباید فونت جداگانه اضافه کنید.
فقط در صورت مشکل (خودمیزبانی بهعنوان راهحل جایگزین)
اگر باندلر شما url() داخل CSS پکیجهای node_modules را resolve نمیکند (نادر — برخی لودرهای CSS سختگیر)، فایل
YekanBakh-VF.woff2 را از node_modules/@partodata/ui/dist/assets/fonts/ به فولدر public خود کپی کرده و
@font-face خودتان را تعریف کنید. این یک fallback عیبیابی است، نه یک گام از نصب.
گام ۵ — 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 لازم نیست.
گام ۶ — تعویض تم با next-themes (اختیاری)
اگر اپ شما بین روشن و تاریک جابهجا میشود، پرتو دو نشانگر برای تم دارد و هر دو باید همگام بمانند: کلاس (.dark/.light) و attribute (data-theme). با next-themes این کار با یک آرایه در attribute انجام میشود:
// app/providers.tsx
'use client'
import { ThemeProvider } from 'next-themes'
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider
attribute={['class', 'data-theme']}
defaultTheme="dark"
themes={['light', 'dark']}
enableSystem={false}
>
{children}
</ThemeProvider>
)
}نکتهی همگامی نشانگرها (ThemeSync)
اگر فقط یک نشانگر را ست کنید (مثلاً تنها attribute="class")، utilityهای dark: و مقادیر توکن ممکن است وسط تعویض با
هم اختلاف پیدا کنند. همیشه attribute={['class', 'data-theme']} بدهید تا هر دو با هم آپدیت شوند. سایت مستندات همین
کار را با یک کامپوننت ThemeSync انجام میدهد.
سوئیچ تم با هوک useTheme:
'use client'
import { useTheme } from 'next-themes'
import { Button } from '@partodata/ui'
export function ThemeSwitcher() {
const { theme, setTheme } = useTheme()
return (
<div className="flex gap-2">
<Button variant={theme === 'light' ? 'primary' : 'outline'} size="sm" onClick={() => setTheme('light')}>
روشن
</Button>
<Button variant={theme === 'dark' ? 'primary' : 'outline'} size="sm" onClick={() => setTheme('dark')}>
تاریک
</Button>
</div>
)
}گام ۷ — تأیید نصب
دو بررسی ساده که ثابت میکند فونت، توکنها، تم، و اسکن Tailwind همگی کار میکنند:
۱. بررسی فونت — در کنسول مرورگر یا یک کامپوننت کلاینت:
'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
}۲. تست دودی سطوح — این بلوک را در هر صفحه رندر کنید:
<div className="bg-surface-100 text-foreground border border-default rounded-md p-4">
اگر این کادر روی یک سطح تیره با حاشیهی محو دیده میشود، توکنها و تم درست بارگذاری شدهاند.
</div>اگر کادر بدون استایل (سفید/بیحاشیه) دیده شد، معمولاً import گام ۲ جا افتاده یا (در Tailwind v3) مسیر dist به content اضافه نشده است.
گام ۸ — فعالسازی گیتهای مصرف
پرتو همان دو گیت استاتیکی را که روی خودش اعمال میکند، از داخل پکیج به مصرفکننده هم میدهد تا اپ شما روی توکن، روی مقیاس، و RTL-صحیح بماند. هر دو را وصل کنید — چیزهای متفاوتی را میگیرند و هیچکدام نصب اضافه نمیخواهند.
پلاگین ESLint
// eslint.config.js
import parto from '@partodata/ui/eslint-plugin'
import tsParser from '@typescript-eslint/parser'
export default [
{
files: ['src/**/*.{ts,tsx}'],
// بدون parser مناسب، espreeِ پیشفرض ESLint 9 قادر به تحلیل JSX/TS نیست
// و قوانین هرگز روی سورس واقعی اجرا نمیشوند.
languageOptions: { parser: tsParser },
plugins: { parto },
rules: parto.configs.recommended.rules,
},
]11 قانون flat-config (9 روی error و 2 روی 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 است که هر اپ جداگانه نگه میداشت.
قرارداد مصرف
این دو گیت، پنج محدودیت ترکیببندی پرتو را اجرا میکنند. برای شرح کامل قرارداد و مرور سهپرسشی، صفحهی قرارداد مصرف را ببینید.
استفاده اولیه
import { Button, Card, CardContent, CardHeader, CardTitle } from '@partodata/ui'
export default function Page() {
return (
<Card>
<CardHeader>
<CardTitle>عنوان کارت</CardTitle>
</CardHeader>
<CardContent>
<p className="text-light">محتوای کارت</p>
<Button className="mt-4">اقدام کنید</Button>
</CardContent>
</Card>
)
}برای اپهای حساس به حجم باندل، هر کامپوننت را از subpath خودش import کنید (مثلاً import { Button } from '@partodata/ui/button') — یک Button از ~۳۴۳KB بارل به ~۱۱KB میرسد.
Tailwind v3 (سازگاری)
اگر هنوز روی Tailwind v3 هستید، بهجای import CSS-first از پیکربندی JS استفاده کنید. این مسیر در محصولات واقعی (مثل panel-new-design) اثبات شده است.
۱. preset + content glob
// tailwind.config.ts
import type { Config } from 'tailwindcss'
import partoConfig from '@partodata/ui/tailwind.config'
const config: Config = {
presets: [partoConfig],
darkMode: ['class', '[data-theme="dark"]'],
content: [
'./app/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
// مسیر dist پکیج تا Tailwind کلاسهای کامپوننتها را purge نکند:
'./node_modules/@partodata/ui/dist/**/*.{js,cjs}',
],
}
export default config۲. import استایل
در فایل CSS اصلی، استایلها را قبل از دایرکتیوهای Tailwind خود import کنید:
@import '@partodata/ui/styles.css';
@tailwind base;
@tailwind components;
@tailwind utilities;۳. پل توکن با var() خام
توکنهای پرتو (v2+) رنگ کامل هستند؛ در Tailwind v3 آنها را با var() خام پل بزنید — هرگز داخل hsl() نپیچید:
// tailwind.config.ts → theme.extend.colors
colors: {
brand: 'var(--brand-default)',
background: 'var(--background-default)',
foreground: 'var(--foreground-default)',
border: 'var(--border-default)',
card: 'var(--background-surface-100)',
}// در کد — رنگ کامل را مستقیم استفاده کنید
className = 'bg-brand text-foreground border-border'
// برای opacity در v3 از color-mix دلخواه استفاده کنید — نه /alpha:
// روی var() خام، Tailwind v3 مدیفایر /alpha را بیصدا نادیده میگیرد و رنگ را با opacity کامل رندر میکند
className =
'bg-[color-mix(in_srgb,var(--brand-default)_10%,transparent)] text-[color-mix(in_srgb,var(--foreground-default)_60%,transparent)]'/alpha فقط روی مسیر v4 کار میکند
مدیفایر /alpha (مثل text-foreground/60) فقط زمانی کار میکند که رنگ از یک ورودی @theme واقعی در Tailwind v4
بیاید (مسیر «گام ۲ — یک import در CSS» در همین صفحه). در Tailwind v3 با پل var() خام (بالا)، /alpha بیصدا شکست
میخورد و رنگ با opacity کامل رندر میشود — همیشه color-mix دلخواه را جایگزین کنید.
۴. darkMode با دو انتخابگر
darkMode باید هم کلاس و هم attribute را بگیرد تا هر دو نشانگر تیرهی پرتو کار کنند:
darkMode: ['class', '[data-theme="dark"]']مشکلات رایج و راهحل
کامپوننتها بدون استایل نمایش داده میشوند
علت: import گام ۲ جا افتاده است.
/* ✅ درست — در 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 نکرده. به گام ۴ و راهحل خودمیزبانی مراجعه کنید.
کلاسهای Tailwind کار نمیکنند (فقط v3)
علت: مسیر dist به content اضافه نشده و Tailwind کلاسها را purge کرده.
content: ['./node_modules/@partodata/ui/dist/**/*.{js,cjs}' /* ... */]layout معکوس است (RTL کار نمیکند)
علت: dir="rtl" روی <html> نیست. CSS Logical Properties به این attribute نیاز دارند.
تم وسط تعویض با خودش اختلاف دارد
علت: فقط یک نشانگر ست شده. attribute={['class', 'data-theme']} را در ThemeProvider بدهید تا کلاس و data-theme همگام بمانند.