المكوّنات
46 مكوّنًا، لكل واحد عرض حيّ: كل الحالات معًا، ومساحة تجربة موصولة بالخصائص الحقيقية، وسطر الاستيراد.
قبل أن تبدأ
مرة واحدة لكل تطبيق
كل بطاقة أدناه تعرض سطر استيراد. هذه الخطوات الثلاث تجعله يعمل.
1. ثبّت الحزمة
terminalnpm i topclassui@beta zodثلاث اعتماديات نظيرة: react وreact-dom وTailwind CSS 4.2 أو أحدث. مشروعك فيه هذه بالفعل، وzod يُثبَّت مع الحزمة.
2. مدخل Tailwind CSS v4
app/globals.css@import "tailwindcss"; @import "topclassui/tokens.css"; @import "topclassui/styles.css";ثلاثة أسطر، بهذا الترتيب، وبلا tailwind.config.js. السطر الثالث يحمل الربط الذي يعطي Tailwind أسماء الأصناف الدلالية.
3. التخطيط الجذري
app/[locale]/layout.tsxconst dir = locale === 'ar' ? 'rtl' : 'ltr' return ( <html lang={locale} dir={dir} data-theme="dark" suppressHydrationWarning> <head><ThemeScript /></head> <body> <TopClassProvider direction={dir} locale={locale}> {children} </TopClassProvider> </body> </html> )مرّر direction وlocale إلى TopClassProvider خاصيتين من التخطيط؛ ولا تقرأهما أبدًا من document.dir داخل effect. يأتي ThemeScript أولًا في <head>: يضبط data-theme قبل أول رسم.
كيف تقرأ البطاقة
كل مكوّن أدناه مرتّب بالطريقة نفسها، فتتعلمها مرة واحدة. خمسة أجزاء، من الأعلى إلى الأسفل.
العرض
المكوّن في أكثر إعداداته شيوعًا، على خلفية الصفحة، لتراه في سياقه.
جرّبه
كل خاصية يمكنك تغييرها، مع قيمها المسموح بها.
الشيفرة
شيفرة JSX التي تنتج العرض فوقها. انسخها والصقها فتعمل.
كل الحالات
التمرير والتركيز والتعطيل والتحميل والخطأ - كلها معًا. لا تحتاج إلى التمرير لمراجعة حالة.
استورده
سطر الاستيراد الواحد من الحزمة، وأمر التثبيت.
الإجراءات
4 مكوّنات
ما ينقر عليه المستخدم. كل واحد منها يُعرض على الخادم ما لم يحتج إلى معالج أحداث في المتصفح.
زر
server - 0 KBخمسة أنماط عبر ست نبرات، يُعرض على الخادم بلا JavaScript. ومن خلال render يصبح رابطًا حقيقيًا بالشكل نفسه.
جرّبه
الشيفرة
import { Button } from 'topclassui/button'
<Button>Save</Button>loading يمنع النقر ويعلن عن نفسه عبر aria-busy، لكنه يحتفظ بلونه كاملًا فلا يبدو معطّلًا.
كل الحالات، بدون تمرير الفأرة
5 حالاتوسم
split - الحجم لم يُقَس بعدشريحة تسمية لموضوع، تُعرض على الخادم. تمرير onRemove يضيف زر إزالة، وهو الجزء الوحيد الذي يعمل في المتصفح.
جرّبه
الشيفرة
import { Tag } from 'topclassui/tag'
<Tag>Design systems</Tag>الوسم العادي يبقى على الخادم، وonRemove هو ما يضيف زر الإزالة، وإخراج الوسم من القائمة مسؤوليتك.
كل الحالات، بدون تمرير الفأرة
حالتانأو استورده من الحزمة
import { Tag } from 'topclassui/tag'ثبّت مرة واحدة:npm i topclassui@beta zod
مفتاح
server - 0 KBغطاء مفتاح لمفتاح يضغطه القارئ، يُعرض على الخادم. والتركيبة مفاتيح متجاورة، بلا جزء تجميع وبلا علامة زائد.
الشيفرة
import { Kbd } from 'topclassui/kbd'
export default function Demo() {
return (
<div className="flex flex-wrap items-center gap-3">
<Kbd>K</Kbd>
<Kbd>Tab</Kbd>
<Kbd>Ctrl</Kbd>
<Kbd>Shift</Kbd>
<Kbd>Enter</Kbd>
<Kbd>Esc</Kbd>
</div>
)
}
الأيقونة داخل المفتاح <Svg> بلا حجم: بقيمتها الافتراضية 1em تتبع نص المفتاح نفسه.
أو استورده من الحزمة
import { Kbd } from 'topclassui/kbd'ثبّت مرة واحدة:npm i topclassui@beta zod
تحكم مقسّم
client - الحجم لم يُقَس بعداختيار واحد من خيارين إلى خمسة خيارات قصيرة، مرسوم كمسار واحد. هو مجموعة أزرار راديو: الأسهم تختار أثناء التنقل، وتنعكس من اليمين إلى اليسار.
جرّبه
الشيفرة
import { SegmentedControlItem, SegmentedControlRoot } from 'topclassui/segmented-control'
<SegmentedControlRoot aria-label="View" defaultValue="board">
<SegmentedControlItem value="board">Board</SegmentedControlItem>
<SegmentedControlItem value="table">Table</SegmentedControlItem>
<SegmentedControlItem value="calendar">Calendar</SegmentedControlItem>
</SegmentedControlRoot>الأسهم تنقل وتختار معًا، وتحت TopClassProvider باتجاه من اليمين إلى اليسار ينتقل السهم الأيمن إلى المقطع السابق.
كل الحالات، بدون تمرير الفأرة
4 حالاتأو استورده من الحزمة
import { SegmentedControlRoot, SegmentedControlItem } from 'topclassui/segmented-control'ثبّت مرة واحدة:npm i topclassui@beta zod
النماذج
13 مكوّنًا
كل عنصر تحكم يحمل اسمًا حقيقيًا ويُرسَل داخل <form> عادي. يعمل Input وTextarea قبل تحميل أي JavaScript، والبقية ترسل قيمتها الافتراضية وتصبح تفاعلية بعد تحميله.
حقل نص
server - 0 KBحقل إدخال أصلي يُعرض على الخادم ولا يشحن أي JavaScript. الأنواع ترفض حقلًا بلا اسم.
جرّبه
الشيفرة
import { Input } from 'topclassui/input'
<div className="grid max-w-sm gap-1">
<label htmlFor="input-playground" className="text-sm font-medium text-body">Email</label>
<Input id="input-playground" name="email" type="email" autoComplete="email" />
</div>خارج Field يحتاج Input إلى id أو aria-label أو aria-labelledby: الأنواع ترفض حقلًا بلا تسمية.
كل الحالات، بدون تمرير الفأرة
6 حالاتأو استورده من الحزمة
import { Input } from 'topclassui/input'ثبّت مرة واحدة:npm i topclassui@beta zod
مربع نص متعدد الأسطر
split - الحجم لم يُقَس بعدمربع نص أصلي يُعرض على الخادم. التمدد التلقائي والعدّاد الحي هما الجزآن الوحيدان اللذان يُحمَّلان في المتصفح.
جرّبه
الشيفرة
import { Textarea } from 'topclassui/textarea'
<div className="grid max-w-sm gap-1">
<label htmlFor="textarea-playground" className="text-sm font-medium text-body">About you</label>
<Textarea id="textarea-playground" name="bio" placeholder="Write a short paragraph" />
</div>وحده هو HTML من الخادم؛ autosize أو showCount هو ما يحمّل نصف العميل الصغير.
كل الحالات، بدون تمرير الفأرة
6 حالاتأو استورده من الحزمة
import { Textarea } from 'topclassui/textarea'ثبّت مرة واحدة:npm i topclassui@beta zod
قائمة اختيار
client - الحجم لم يُقَس بعداختيار واحد من قائمة بيانات بسيطة. تبقى القائمة المنبثقة داخل الشاشة وتمرّر نفسها، بارتفاع سبعة صفوف افتراضيًا.
جرّبه
الشيفرة
import { Select } from 'topclassui/select'
<div className="grid justify-items-start gap-1.5">
<label htmlFor="select-playground" className="text-sm text-body">Country</label>
<Select id="select-playground" items={COUNTRIES} defaultValue="jo" />
</div>items بيانات بسيطة، فتستطيع صفحة خادم أن تعرض Select: لا تعبر أي دالة إلى المتصفح.
كل الحالات، بدون تمرير الفأرة
4 حالاتأو استورده من الحزمة
import { Select } from 'topclassui/select'ثبّت مرة واحدة:npm i topclassui@beta zod
قائمة اختيار مع بحث
client - الحجم لم يُقَس بعدحقل نص يصفّي قائمة أثناء الكتابة. يعرض الصفوف الظاهرة فقط بعد 200 خيار، ويستدعي loadMore لجلب الصفحة التالية.
جرّبه
الشيفرة
import { Combobox } from 'topclassui/combobox'
<div className="grid justify-items-start gap-1">
<label htmlFor="combobox-playground" className="text-sm font-medium text-body">Framework</label>
<Combobox id="combobox-playground" items={FRAMEWORKS} placeholder="Search frameworks" />
</div>تجري التصفية على الخيارات المحمّلة حتى الآن؛ وتصل الصفحة التالية عبر hasMore وloadMore.
كل الحالات، بدون تمرير الفأرة
4 حالاتأو استورده من الحزمة
import { Combobox } from 'topclassui/combobox'ثبّت مرة واحدة:npm i topclassui@beta zod
مربع اختيار
client - الحجم لم يُقَس بعدمحدد أو غير محدد أو مختلط. حقل مخفي يرسل القيمة الافتراضية قبل الترطيب، وإعادة ضبط النموذج تعيدها.
جرّبه
الشيفرة
import { Checkbox } from 'topclassui/checkbox'
<div className="flex items-center gap-2">
<Checkbox id="checkbox-playground" defaultChecked />
<label htmlFor="checkbox-playground" className="text-sm text-body">I accept the terms</label>
</div>المربع زر أصلي <button role="checkbox">، وبجانبه مربع اختيار مخفي هو ما يرسله النموذج.
كل الحالات، بدون تمرير الفأرة
9 حالاتأو استورده من الحزمة
import { Checkbox } from 'topclassui/checkbox'ثبّت مرة واحدة:npm i topclassui@beta zod
مجموعة اختيار فردي
client - الحجم لم يُقَس بعداختيار واحد من خيارات قليلة معروضة كلها معًا. للمجموعة محطة Tab واحدة، والزر المحدد هو تلك المحطة قبل الترطيب.
جرّبه
الشيفرة
import { RadioGroupItem, RadioGroupRoot } from 'topclassui/radio-group'
<div className="grid justify-items-start gap-2">
<span id="radio-group-playground-label" className="text-sm text-body">Billing</span>
<RadioGroupRoot aria-labelledby="radio-group-playground-label" defaultValue="yearly">
<RadioGroupItem value="monthly">Monthly</RadioGroupItem>
<RadioGroupItem value="yearly">Yearly</RadioGroupItem>
<RadioGroupItem value="lifetime">Lifetime</RadioGroupItem>
</RadioGroupRoot>
</div>المجموعة تحمل الحالة والنموذج؛ وكل RadioGroupItem يحمل value الخاص به ونصه فقط.
كل الحالات، بدون تمرير الفأرة
4 حالاتأو استورده من الحزمة
import { RadioGroupRoot, RadioGroupItem } from 'topclassui/radio-group'ثبّت مرة واحدة:npm i topclassui@beta zod
مفتاح تبديل
client - الحجم لم يُقَس بعدمفتاح تشغيل وإطفاء لإعداد يسري فورًا. يتحرك مقبضه في الاتجاه الصحيح في التخطيطات من اليمين إلى اليسار.
جرّبه
الشيفرة
import { Switch } from 'topclassui/switch'
<div className="flex items-center gap-2">
<Switch id="switch-playground" defaultChecked />
<label htmlFor="switch-playground" className="text-sm text-body">Weekly digest</label>
</div>المفتاحان xs وsm يكبران إلى هدف بمقدار 44px تحت مؤشر لمس، فيبقى المقبض سهل اللمس.
كل الحالات، بدون تمرير الفأرة
7 حالاتأو استورده من الحزمة
import { Switch } from 'topclassui/switch'ثبّت مرة واحدة:npm i topclassui@beta zod
شريط تمرير
client - الحجم لم يُقَس بعدقيمة واحدة أو نطاق من قيمتين. مفاتيح الأسهم والضغط على المسار يصلان إلى النتيجة نفسها التي يصل إليها السحب.
جرّبه
الشيفرة
import { Slider } from 'topclassui/slider'
<div className="grid justify-items-start gap-2">
<span id="slider-playground-label" className="text-sm text-body">Volume</span>
<Slider aria-labelledby="slider-playground-label" defaultValue={60} />
</div>حقل نطاق مخفي يحمل القيمة، فيُرسل الشريط مع نموذجه؛ ويتحرك المقبض بعد ترطيب الصفحة.
كل الحالات، بدون تمرير الفأرة
4 حالاتأو استورده من الحزمة
import { Slider } from 'topclassui/slider'ثبّت مرة واحدة:npm i topclassui@beta zod
حقل رقمي
client - الحجم لم يُقَس بعدرقم بين حدود معروفة، يُكتب أو يُزاد خطوة خطوة. حقل مخفي يحمل الرقم المجرد، فيُرسل النموذج قبل الترطيب.
جرّبه
الشيفرة
import { NumberField } from 'topclassui/number-field'
<div className="grid justify-items-start gap-2">
<label htmlFor="number-field-playground-guests" className="text-sm text-body">Guests</label>
<NumberField id="number-field-playground-guests" defaultValue={2} />
</div>min وmax وstep تُحصر أثناء التشغيل، فلا تستطيع قيمة مكتوبة أو ممرَّرة أن تكسر الحقل.
كل الحالات، بدون تمرير الفأرة
4 حالاتأو استورده من الحزمة
import { NumberField } from 'topclassui/number-field'ثبّت مرة واحدة:npm i topclassui@beta zod
حقل رمز التحقق
client - الحجم لم يُقَس بعدرمز يُستخدم مرة واحدة، حرف في كل خانة. اللصق يملأ كل الخانات، وحقل مخفي واحد يحمل الرمز إلى النموذج.
جرّبه
الشيفرة
import { OTPField } from 'topclassui/otp-field'
<div className="grid justify-items-start gap-2">
<label htmlFor="otp-playground-code" className="text-sm text-body">Verification code</label>
<OTPField id="otp-playground-code" />
</div>اسم التصدير هو OTPField. داخل نموذج يرسل الحقل المخفي ما كُتب حتى الآن، حتى لو كان الرمز ناقصًا.
كل الحالات، بدون تمرير الفأرة
6 حالاتأو استورده من الحزمة
import { OtpField } from 'topclassui/otp-field'ثبّت مرة واحدة:npm i topclassui@beta zod
منتقي التاريخ
client - الحجم لم يُقَس بعد, عند الطلبيوم واحد يُختار من شهر. يُحمَّل التقويم عند فتحه أول مرة، وكل اسم شهر وكل رقم يأتي من Intl.
جرّبه
الشيفرة
import { DatePicker } from 'topclassui/date-picker'
<div className="grid justify-items-start gap-1.5">
<label htmlFor="date-picker-playground" className="text-sm text-body">Check-in</label>
<DatePicker id="date-picker-playground" defaultValue="2026-09-22" />
</div>القيمة تاريخ بصيغة ISO، أي YYYY-MM-DD؛ والصيغة المعروضة تأتي من لغة المزوّد.
كل الحالات، بدون تمرير الفأرة
3 حالاتأو استورده من الحزمة
import { DatePicker } from 'topclassui/date-picker'ثبّت مرة واحدة:npm i topclassui@beta zod
رفع الملفات
client - الحجم لم يُقَس بعدمنطقة إفلات هي حقل ملفات حقيقي، فتعمل وتُرسل قبل تشغيل أي سكربت. والملفات المختارة تظهر في قائمة.
جرّبه
الشيفرة
import { FileUpload } from 'topclassui/file-upload'
<div className="grid gap-1.5">
<label htmlFor="file-upload-playground" className="text-sm font-medium text-body">Attachment</label>
<FileUpload id="file-upload-playground" name="attachment" accept=".png,.jpg,.jpeg,.pdf">
PNG, JPG or PDF
</FileUpload>
</div>maxSize بالبايت، ويُتحقق منه في المتصفح فقط: تحقّق منه مجددًا في الإجراء.
كل الحالات، بدون تمرير الفأرة
حالتانأو استورده من الحزمة
import { FileUpload } from 'topclassui/file-upload'ثبّت مرة واحدة:npm i topclassui@beta zod
التقييم
split - الحجم لم يُقَس بعدنجوم القراءة فقط هي HTML من الخادم ولا تشحن أي سكربت. وحده التقييم القابل للتعديل يترطّب، كمجموعة أزرار راديو أصلية.
جرّبه
الشيفرة
import { Rating } from 'topclassui/rating'
import type { RatingDesignProps } from 'topclassui/rating'
/** The site's Try it stage: each schema prop arrives from a control, and an absent one keeps its default. */
export default function Demo(props: RatingDesignProps = {}) {
const { readOnly, defaultValue, disabled, ...shown } = props
if (readOnly === false) {
return <Rating defaultValue={defaultValue ?? 3} disabled={disabled} {...shown} readOnly={false} aria-label="Rate this course" />
}
return <Rating value={3.5} {...shown} />
}
value وحده يعرض النجوم على الخادم؛ ووحده readOnly={false} يشحن نصف العميل.
كل الحالات، بدون تمرير الفأرة
6 حالاتأو استورده من الحزمة
import { Rating } from 'topclassui/rating'ثبّت مرة واحدة:npm i topclassui@beta zod
العرض
7 مكوّنات
قطع للعرض. كلها تقريبًا تُعرض على الخادم ولا ترسل أي JavaScript، وحين يضيف أحدها زرًا، مثل Tag القابل للإزالة، تقول ذلك الشارة على بطاقته.
شارة
server - 0 KBتسمية حالة قصيرة بثلاثة أنماط عبر ست نبرات. تُعرض على الخادم ولا تُرسل أي JavaScript إلى المتصفح.
جرّبه
الشيفرة
import { Badge } from 'topclassui/badge'
<Badge>Paid</Badge>لا تحمل الشارة أي دور ARIA: النص يحمل المعنى، واللون وحده لا يكفي أبدًا.
كل الحالات، بدون تمرير الفأرة
3 حالاتأو استورده من الحزمة
import { Badge } from 'topclassui/badge'ثبّت مرة واحدة:npm i topclassui@beta zod
صورة رمزية
split - الحجم لم يُقَس بعدصورة أو أحرف أولى أو أيقونة داخل دائرة، بالأحجام الخمسة نفسها لكل عنصر تحكم، فتصطف الصورة الرمزية مع الزر في الصف نفسه.
جرّبه
الشيفرة
import { AvatarFallback, AvatarImage, AvatarRoot } from 'topclassui/avatar'
<AvatarRoot>
<AvatarImage src={PORTRAIT} alt="Omar Abed" />
<AvatarFallback>OA</AvatarFallback>
</AvatarRoot>يظهر البديل أثناء تحميل الصورة وبعد فشلها؛ والأحرف الأولى هي محتواه.
كل الحالات، بدون تمرير الفأرة
5 حالاتأو استورده من الحزمة
import { AvatarRoot, AvatarImage, AvatarFallback, AvatarSkeleton } from 'topclassui/avatar'ثبّت مرة واحدة:npm i topclassui@beta zod
مجموعة صور رمزية
server - 0 KBصور رمزية متداخلة مع عدّاد +N للبقية. اعرض كل الأعضاء؛ وتُحصر قيمة max بين 1 و20.
جرّبه
الشيفرة
import { AvatarFallback, AvatarRoot } from 'topclassui/avatar'
import { AvatarGroup } from 'topclassui/avatar-group'
<AvatarGroup aria-label="Project team">
{TEAM.map(([initials, name]) => (
<AvatarRoot key={initials} size={props.size} role="img" aria-label={name}>
<AvatarFallback>{initials}</AvatarFallback>
</AvatarRoot>
))}
</AvatarGroup>تعدّ max العناصر الأبناء، فاعرض كل عضو ودع المجموعة تخفي الفائض.
كل الحالات، بدون تمرير الفأرة
حالة واحدةأو استورده من الحزمة
import { AvatarGroup } from 'topclassui/avatar-group'ثبّت مرة واحدة:npm i topclassui@beta zod
بطاقة
server - 0 KBسطح لمحتوى يجمعه موضوع واحد، باستدارة أكبر من استدارة عنصر التحكم كي لا تبدو البطاقة زرًا أبدًا.
Storage
الشيفرة
import { CardBody, CardHeader, CardRoot } from 'topclassui/card'
/** The first drawing: a title and a line of text, with nothing to press. */
export default function Demo() {
return (
<div className="max-w-xs">
<CardRoot>
<CardHeader>
<h2>Storage</h2>
</CardHeader>
<CardBody>You have used 18 GB of the 50 GB in your plan.</CardBody>
</CardRoot>
</div>
)
}
تحدد البطاقة خط عنوانها وتترك لك مستوى العنوان: ضع h2 أو h3 داخل CardHeader.
كل الحالات، بدون تمرير الفأرة
3 حالاتStorage
Version 0.4
أو استورده من الحزمة
import { CardRoot, CardHeader, CardBody, CardFooter, CardSkeleton } from 'topclassui/card'ثبّت مرة واحدة:npm i topclassui@beta zod
فاصل
server - 0 KBخط رفيع بين مجموعتين، عرضيًا في عمود أو طوليًا في صف. زخرفي افتراضيًا، فلا يُعلَن لقارئ الشاشة.
جرّبه
الشيفرة
import { Separator } from 'topclassui/separator'
<div className="flex min-h-5 flex-wrap items-center gap-3 text-sm text-body">
<span>Profile</span>
<Separator />
<span>Billing</span>
</div>يأخذ الفاصل العمودي ارتفاعه من صف flex أو grid؛ أعطِ الصف ارتفاعًا أو دعه يتمدد.
كل الحالات، بدون تمرير الفأرة
حالتانأو استورده من الحزمة
import { Separator } from 'topclassui/separator'ثبّت مرة واحدة:npm i topclassui@beta zod
هيكل التحميل
server - 0 KBعنصر نائب يحجز شكل المحتوى أثناء تحميله: أسطر نص، أو دائرة، أو مستطيل، أو بطاقة.
جرّبه
الشيفرة
import { Skeleton } from 'topclassui/skeleton'
<div className="max-w-sm">
<Skeleton />
</div>أعطِ الهيكل الأبعاد الحقيقية عبر className؛ تُحصر rows بين 1 و50، والحجم الخاطئ يعني إزاحة في التخطيط.
كل الحالات، بدون تمرير الفأرة
حالة واحدةأو استورده من الحزمة
import { Skeleton } from 'topclassui/skeleton'ثبّت مرة واحدة:npm i topclassui@beta zod
كتلة شيفرة
split - الحجم لم يُقَس بعدشيفرة في صندوق خاص بها، تُقرأ من اليسار إلى اليمين في أي صفحة. تُعرض على الخادم، وزر النسخ هو الشيفرة الوحيدة في المتصفح.
export default async function Page() {
const invoices = await getInvoices({ status: 'overdue', sortBy: 'dueDate', order: 'descending' })
return <InvoiceTable items={invoices} />
}جرّبه
الشيفرة
import type { CodeBlockDesignProps } from 'topclassui/code-block'
import { CodeBlock } from 'topclassui/code-block'
/** The docs stage: the Try it controls hand their values in as props. One line is long, so wrap shows. */
export default function Demo(props: CodeBlockDesignProps) {
return (
<CodeBlock lang="tsx" title="app/invoices/page.tsx" {...props}>
{`export default async function Page() {
const invoices = await getInvoices({ status: 'overdue', sortBy: 'dueDate', order: 'descending' })
return <InvoiceTable items={invoices} />
}`}
</CodeBlock>
)
}
مرّر الشيفرة ابنًا بصيغة template literal. لا يوجد ملوّن مدمج: مرّر العقد الملوّنة عبر highlighted.
كل الحالات، بدون تمرير الفأرة
3 حالاتexport default async function Page() {
const invoices = await getInvoices()
return <InvoiceTable items={invoices} />
}export const columns = ['number', 'customer', 'issued', 'due', 'amount', 'status', 'actions'] as constexport default async function Page() {
const invoices = await getInvoices()
return <InvoiceTable items={invoices} />
}أو استورده من الحزمة
import { CodeBlock } from 'topclassui/code-block'ثبّت مرة واحدة:npm i topclassui@beta zod
البيانات
4 مكوّنات
كل مكوّن قائمة يعمل بالطريقة نفسها: خاصية للصفوف الظاهرة، وعرض نافذي فوق 200 عنصر، ودالة loadMore. ولا يجلب البيانات بنفسه أبدًا.
جدول
split - الحجم لم يُقَس بعدجدول HTML حقيقي يُعرض على الخادم: الترتيب رابط في كل عنوان عمود، والعمود الأول يمكن تثبيته، ولا يشحن أي JavaScript.
جرّبه
الشيفرة
import { Table, type TableDesignProps } from 'topclassui/table'
<Table
caption="Quarterly revenue by region"
items={regions}
getKey={(region) => region.name}
columns={[
{ key: 'name', header: 'Region', rowHeader: true, cell: (region) => region.name },
{ key: 'q1', header: 'Q1', align: 'end', cell: (region) => region.q1 },
{ key: 'q2', header: 'Q2', align: 'end', cell: (region) => region.q2 },
{ key: 'q3', header: 'Q3', align: 'end', cell: (region) => region.q3 },
{ key: 'q4', header: 'Q4', align: 'end', cell: (region) => region.q4 },
{ key: 'total', header: 'Full year', align: 'end', cell: (region) => region.total },
]}
/>الترتيب رابط يجيب عنه الخادم بالصفوف في ترتيبها الجديد، فلا يكلّف الجدول المرتّب أي JavaScript.
كل الحالات، بدون تمرير الفأرة
7 حالاتأو استورده من الحزمة
import { Table } from 'topclassui/table'ثبّت مرة واحدة:npm i topclassui@beta zod
عرض شجري
client - الحجم لم يُقَس بعدعقد في مستويات تُفتح وتُغلق. الفروع المفتوحة تُسطَّح في قائمة واحدة تعرض ما يظهر فقط، فتبقى 10,000 عقدة سريعة.
جرّبه
الشيفرة
import { fileText } from 'topclassui/icons/file-text'
import { folder } from 'topclassui/icons/folder'
import { Svg } from 'topclassui/svg'
import { TreeView, type TreeViewDesignProps } from 'topclassui/tree-view'
<TreeView aria-label="Files" items={ITEMS} defaultExpanded={['src', 'app']} />items بيانات عادية وأيقونة العقدة عنصر، فيمرّر مكوّن الخادم الشجرة كما هي، ولا يوجد getKey.
كل الحالات، بدون تمرير الفأرة
4 حالاتأو استورده من الحزمة
import { TreeView } from 'topclassui/tree-view'ثبّت مرة واحدة:npm i topclassui@beta zod
خط زمني
server - 0 KBمسار من الأحداث المؤرّخة، لكل حدث علامة بنبرته. يُعرض على الخادم وينعكس وحده في التخطيطات من اليمين إلى اليسار.
- DoneProject created
- DoneFirst deploy
- CurrentIn reviewToday
- Not startedLaunchNot scheduled
الشيفرة
import { Timeline } from 'topclassui/timeline'
const EVENTS = [
{ id: 'created', title: 'Project created', time: <time dateTime="2026-10-02T09:12">2 October, 09:12</time>, status: 'done' },
{ id: 'deployed', title: 'First deploy', time: <time dateTime="2026-10-04T16:40">4 October, 16:40</time>, status: 'done' },
{ id: 'review', title: 'In review', time: 'Today', status: 'current' },
{ id: 'launch', title: 'Launch', time: 'Not scheduled', status: 'todo' },
] as const
/** The drawn rail: done filled in success, the current step a dot in a ring, and a hollow todo.
* A `<time>` gives a date a machine can read; each status is also said in hidden text. */
export default function Demo() {
return <Timeline items={EVENTS} getKey={(event) => event.id} />
}
أيقونة العنصر عنصرٌ، مثل <Svg icon={check} size="xs" />، لا اسم أيقونة: لا يُشحن أي محمّل أيقونات.
كل الحالات، بدون تمرير الفأرة
6 حالات- DoneDoneA filled dot in success
- CurrentCurrentA dot in a ring, read as the current step
- Not startedTo doA hollow dot
- A tone of its ownNo status, the danger tone
- DoneAn iconTakes the tone of its status
- Not startedAn icon to do
أو استورده من الحزمة
import { Timeline } from 'topclassui/timeline'ثبّت مرة واحدة:npm i topclassui@beta zod
مؤشر الخطوات
server - 0 KBأين يقف القارئ في مهمة من بضع خطوات. الخطوة المكتملة تحمل علامة صح لا لونًا فقط، فتبقى واضحة في لقطة شاشة رمادية.
- AccountCompleted
- Profile
- TeamNot started
- DoneNot started
جرّبه
الشيفرة
import { Stepper, type StepperDesignProps } from 'topclassui/stepper'
<Stepper items={STEPS} value="profile" aria-label="Sign-up" />مؤشر الخطوات يُعرض على الخادم: مرّر value، أي معرّف الخطوة الحالية. هو يعرض التقدّم ولا ينقل بين الصفحات.
كل الحالات، بدون تمرير الفأرة
4 حالات- Cart
- AddressNot started
- PaymentNot started
- CartCompleted
- Address
- PaymentNot started
- CartCompleted
- AddressNeeds attention
- PaymentNot started
- CartCompleted
- AddressCompleted
- PaymentCompleted
أو استورده من الحزمة
import { Stepper } from 'topclassui/stepper'ثبّت مرة واحدة:npm i topclassui@beta zod
التنبيهات
6 مكوّنات
الحالة والتقدم والحالات الفارغة. يعلن Toast وProgress عن نفسيهما لقارئ الشاشة، والبقية تبقى صامتة.
تنبيه
server - 0 KBرسالة تنتمي إلى الصفحة، بست نبرات. لكلٍّ من المعلومة والنجاح والتحذير والخطر أيقونتها الخاصة، فتُقرأ الرسالة دون الاعتماد على اللون.
Your trial ends in three days
جرّبه
الشيفرة
import { Alert } from 'topclassui/alert'
<Alert title="Your trial ends in three days">
Deploys pause when it does. Adding a payment method turns them back on.
</Alert>ليس منطقة حية: إنه جزء من محتوى الصفحة. مرّر role="alert" إذا ظهر بعد التحميل، أو أعلن الحدث بإشعار.
كل الحالات، بدون تمرير الفأرة
13 حالةSoft
Solid
Outline
Primary
Neutral
Success
Warning
Danger
Info
Title only
No icon
With actions
أو استورده من الحزمة
import { Alert } from 'topclassui/alert'ثبّت مرة واحدة:npm i topclassui@beta zod
إشعار
client - الحجم لم يُقَس بعدإشعار قصير بأن شيئًا حدث. يظهر في زاوية ويغادر وحده، وفيه دائمًا زر إغلاق، لأن السحب بالإصبع حركة جرّ.
جرّبه
الشيفرة
'use client'
import { Button } from 'topclassui/button'
import { toast } from 'topclassui/toast'
import type { ToastDesignProps } from 'topclassui/toast'
/** The docs stage: the Try it controls set the options of the toast the press adds. */
export default function Demo(props: ToastDesignProps = {}) {
return (
<Button
variant="outline"
onClick={() => { toast.add({ ...props, title: 'Project created', description: 'It is ready for its first deploy.' }) }}
>
Show a toast
</Button>
)
}
TopClassProvider يركّب منطقة العرض الوحيدة، فلا شيء عليك تركيبه؛ وtoast.add يعمل من أي وحدة في المتصفح، داخل React أو خارجه.
كل الحالات، بدون تمرير الفأرة
11 حالةأو استورده من الحزمة
import { Toast } from 'topclassui/toast'ثبّت مرة واحدة:npm i topclassui@beta zod
شريط إعلان
split - الحجم لم يُقَس بعدرسالة واحدة للصفحة كلها، بعرض كامل فوق الترويسة. يقع في تدفق الصفحة، فيدفعها إلى الأسفل ولا يغطي شيئًا.
جرّبه
الشيفرة
import { bell } from 'topclassui/icons/bell'
import { Button } from 'topclassui/button'
import { Svg } from 'topclassui/svg'
import { Banner } from 'topclassui/banner'
<Banner
id="banner-playground"
aria-label="Version 2 announcement"
icon={<Svg icon={bell} size="xs" />}
action={<Button size="sm" variant="outline" tone="neutral" render={<a href="/changelog" />}>Read the changelog</Button>}
>
Version 2 is out, with faster builds and a new theme editor.
</Banner>الإخفاء يُحفظ حسب المعرّف في هذا المتصفح، فغيّر المعرّف عندما تتغير الرسالة.
كل الحالات، بدون تمرير الفأرة
9 حالاتشريط التقدم
server - 0 KBإلى أين وصلت المهمة: مسار محايد عليه شريط بلون النبرة، يقرؤه قارئ الشاشة شريطَ تقدم من 0 إلى 100.
جرّبه
الشيفرة
import { Progress } from 'topclassui/progress'
<div className="grid max-w-sm">
<Progress aria-label="Upload" />
</div>لا يعرض أي نص ويحصر القيمة بين 0 و100، فسمِّه بـ aria-label أو aria-labelledby.
كل الحالات، بدون تمرير الفأرة
5 حالاتأو استورده من الحزمة
import { Progress } from 'topclassui/progress'ثبّت مرة واحدة:npm i topclassui@beta zod
مؤشر التحميل
server - 0 KBللانتظار مجهول المدة. مؤشر التحميل أيقونة، فيأخذ أحجام الأيقونات من 16 إلى 40 بكسل، ويمكنه أن يتبع النص الذي يجلس فيه.
جرّبه
الشيفرة
import { Spinner } from 'topclassui/spinner'
<Spinner />فضّل Skeleton عندما يكون شكل القادم معروفًا؛ مؤشر التحميل للانتظار المجهول.
كل الحالات، بدون تمرير الفأرة
3 حالاتDecorative, in the text colour
أو استورده من الحزمة
import { Spinner } from 'topclassui/spinner'ثبّت مرة واحدة:npm i topclassui@beta zod
الحالة الفارغة
server - 0 KBما تعرضه القائمة أو الجدول أو البحث حين لا يوجد ما يُعرض: أيقونة وسطر وجملة وإجراء من عندك.
Nothing here yet
الشيفرة
import { EmptyState } from 'topclassui/empty-state'
/** No props at all: the tray icon, then the English title and description from labels. */
export default function Demo() {
return (
<div className="max-w-85">
<EmptyState />
</div>
)
}
بلا خصائص يعرض أيقونة الدُّرج ونصّي labels.state؛ وعنوانك الخاص يُسقط الوصف الافتراضي.
كل الحالات، بدون تمرير الفأرة
5 حالاتNothing here yet
Title only
With action
Failed
Filtered
أو استورده من الحزمة
import { EmptyState } from 'topclassui/empty-state'ثبّت مرة واحدة:npm i topclassui@beta zod
الطبقات
6 مكوّنات
تشترك الطبقات في قاعدة تمرير واحدة: تتمرر الطبقة ولا تتمرر الصفحة خلفها. والموضع منطقي، فينعكس في العربية.
مربع حوار
client - الحجم لم يُقَس بعدمربع حوار يحجب الصفحة، يتمرّر متنه بين رأس وتذييل ثابتين، بينما تبقى الصفحة خلفه ساكنة.
جرّبه
الشيفرة
import { Button } from 'topclassui/button'
import {
<DialogRoot>
<DialogTrigger render={<Button>Publish</Button>} />
<DialogPopup>
<DialogHeader>
<DialogTitle>Publish changes</DialogTitle>
<DialogDescription>Your changes go live right away.</DialogDescription>
</DialogHeader>
<DialogBody>11 files changed since the last publish.</DialogBody>
<DialogFooter>
<DialogClose render={<Button variant="outline">Cancel</Button>} />
<DialogClose render={<Button>Publish now</Button>} />
</DialogFooter>
</DialogPopup>
</DialogRoot>غلّف الأجزاء بنموذج يحمل الصنف contents، فيُرسَل إجراء الخادم من التذييل دون أي معالج في المتصفح.
كل الحالات، بدون تمرير الفأرة
5 حالاتأو استورده من الحزمة
import { DialogRoot, DialogBody, DialogClose, DialogDescription, DialogFooter, DialogHeader, DialogPopup, DialogTitle, DialogTrigger } from 'topclassui/dialog'ثبّت مرة واحدة:npm i topclassui@beta zod
درج
client - الحجم لم يُقَس بعدمربع حوار ينزلق من حافة منطقية، فينعكس في العربية دون أي تعديل في الشيفرة. يُغلق بالسحب أو بزر الإغلاق.
جرّبه
الشيفرة
import { Button } from 'topclassui/button'
import {
<DrawerRoot>
<DrawerTrigger render={<Button variant="outline">Filters</Button>} />
<DrawerPopup>
<DrawerHeader>
<DrawerTitle>Filters</DrawerTitle>
<DrawerDescription>Narrow the list of projects.</DrawerDescription>
</DrawerHeader>
<DrawerBody className="grid content-start gap-2.5">
<p>Status</p>
<p>Owner</p>
<p>Updated</p>
</DrawerBody>
<DrawerFooter>
<DrawerClose render={<Button variant="outline">Reset</Button>} />
<DrawerClose render={<Button>Apply</Button>} />
</DrawerFooter>
</DrawerPopup>
</DrawerRoot>placement منطقي: start يفتح من اليسار في الإنجليزية ومن اليمين في العربية. لا left ولا right أبدًا.
كل الحالات، بدون تمرير الفأرة
5 حالاتأو استورده من الحزمة
import { DrawerRoot, DrawerBody, DrawerClose, DrawerDescription, DrawerFooter, DrawerHeader, DrawerPopup, DrawerTitle, DrawerTrigger } from 'topclassui/drawer'ثبّت مرة واحدة:npm i topclassui@beta zod
نافذة منبثقة
client - الحجم لم يُقَس بعدلوحة مثبّتة بجانب مشغّلها لرابط أو حقل أو إجراء. تنقلب وتتقلّص إلى المساحة المتاحة بدلًا من أن تفيض.
جرّبه
الشيفرة
import { Button } from 'topclassui/button'
import { PopoverDescription, PopoverPopup, PopoverRoot, PopoverTitle, PopoverTrigger } from 'topclassui/popover'
<PopoverRoot>
<PopoverTrigger openOnHover render={<Button variant="outline">Share</Button>} />
<PopoverPopup>
<PopoverTitle>Share this page</PopoverTitle>
<PopoverDescription>Anyone with the link can read it. Only your team can edit.</PopoverDescription>
<Button size="sm" className="self-start">Copy link</Button>
</PopoverPopup>
</PopoverRoot>التلميح نص فقط: أبناؤه مكتوبون كنص. كل ما فيه رابط أو عنصر تحكم هو نافذة منبثقة.
كل الحالات، بدون تمرير الفأرة
5 حالاتأو استورده من الحزمة
import { PopoverRoot, PopoverClose, PopoverDescription, PopoverPopup, PopoverTitle, PopoverTrigger } from 'topclassui/popover'ثبّت مرة واحدة:npm i topclassui@beta zod
تلميح
client - الحجم لم يُقَس بعدسطر أو سطران من النص عند التمرير أو التركيز: اسم عنصر تحكم بأيقونة فقط، أو تسمية مقطوعة.
جرّبه
الشيفرة
import { linkSimple } from 'topclassui/icons/link-simple'
import { Button } from 'topclassui/button'
import { Svg } from 'topclassui/svg'
import { TooltipPopup, TooltipRoot, TooltipTrigger } from 'topclassui/tooltip'
<TooltipRoot>
<TooltipTrigger
render={
<Button variant="ghost" aria-label="Copy link">
<Svg icon={linkSimple} />
</Button>
}
/>
<TooltipPopup>Copy link</TooltipPopup>
</TooltipRoot>شاشات اللمس لا تعرض التلميح أبدًا، فيجب أن يكرّر اسم المشغّل النص نفسه: أعطِ الزر ذا الأيقونة فقط aria-label.
كل الحالات، بدون تمرير الفأرة
4 حالاتأو استورده من الحزمة
import { TooltipRoot, TooltipPopup, TooltipTrigger } from 'topclassui/tooltip'ثبّت مرة واحدة:npm i topclassui@beta zod
قائمة منسدلة
client - الحجم لم يُقَس بعدقائمة إجراءات وروابط خلف زر واحد. صفوف الروابط روابط حقيقية، فيعمل الفتح في علامة تبويب جديدة والنقر بالزر الأوسط.
جرّبه
الشيفرة
import { copy } from 'topclassui/icons/copy'
import { gear } from 'topclassui/icons/gear'
import { trash } from 'topclassui/icons/trash'
import { user } from 'topclassui/icons/user'
import { Button } from 'topclassui/button'
import { Svg } from 'topclassui/svg'
import { DropdownItem, DropdownPopup, DropdownRoot, DropdownSeparator, DropdownTrigger } from 'topclassui/dropdown'
<DropdownRoot>
<DropdownTrigger openOnHover render={<Button variant="outline">Actions</Button>} />
<DropdownPopup>
<DropdownItem icon={<Svg icon={user} />} shortcut="Ctrl P">Profile</DropdownItem>
<DropdownItem icon={<Svg icon={gear} />}>Settings</DropdownItem>
<DropdownItem icon={<Svg icon={copy} />}>Duplicate</DropdownItem>
<DropdownSeparator />
<DropdownItem icon={<Svg icon={trash} />}>Delete</DropdownItem>
</DropdownPopup>
</DropdownRoot>عناصر href وإجراء الخادم تعمل من صفحة خادم؛ أما onSelect فدالة عادية تحتاج ملف عميل.
كل الحالات، بدون تمرير الفأرة
4 حالاتأو استورده من الحزمة
import { DropdownRoot, DropdownGroup, DropdownGroupLabel, DropdownItem, DropdownLinkItem, DropdownMenu, DropdownPopup, DropdownSeparator, DropdownTrigger } from 'topclassui/dropdown'ثبّت مرة واحدة:npm i topclassui@beta zod
لوحة الأوامر
client - الحجم لم يُقَس بعد, عند الطلبلوحة أوامر قابلة للبحث تُفتح بـ Ctrl+K أو Cmd+K. تُحمَّل عند أول فتح لها، لا مع الصفحة أبدًا.
جرّبه
الشيفرة
'use client'
import { useState } from 'react'
import { magnifyingGlass } from 'topclassui/icons/magnifying-glass'
import { Button } from 'topclassui/button'
import { Svg } from 'topclassui/svg'
import { Command } from 'topclassui/command'
import type { CommandDesignProps } from 'topclassui/command'
interface Entry { id: string, label: string }
const COMMANDS: Entry[] = [
{ id: 'button', label: 'Button' },
{ id: 'dialog', label: 'Dialog' },
{ id: 'drawer', label: 'Drawer' },
{ id: 'popover', label: 'Popover' },
{ id: 'tabs', label: 'Tabs' },
{ id: 'accordion', label: 'Accordion' },
{ id: 'table', label: 'Table' },
{ id: 'pagination', label: 'Pagination' },
{ id: 'breadcrumb', label: 'Breadcrumb' },
{ id: 'tooltip', label: 'Tooltip' },
]
/** The docs stage: the basic palette, with the schema's props passed through, so the Try it
* controls drive it. defaultOpen is where the open state starts; the Button opens it too. */
export default function Demo({ visibleRows, virtualize, shortcut, defaultOpen }: CommandDesignProps = {}) {
const [open, setOpen] = useState(defaultOpen ?? false)
return (
<div className="grid justify-items-start gap-3">
<Button variant="outline" onClick={() => { setOpen(true) }}><Svg icon={magnifyingGlass} />Search</Button>
<Command
items={COMMANDS}
getKey={(command) => command.id}
getLabel={(command) => command.label}
onSelect={() => undefined}
visibleRows={visibleRows}
virtualize={virtualize}
shortcut={shortcut}
open={open}
onOpenChange={setOpen}
/>
</div>
)
}
getKey وgetLabel وonSelect دوال، لذا تُضبط اللوحة داخل ملف عميل.
كل الحالات، بدون تمرير الفأرة
6 حالاتأو استورده من الحزمة
import { Command } from 'topclassui/command'ثبّت مرة واحدة:npm i topclassui@beta zod
التنقّل
6 مكوّنات
الروابط روابط. كل واحد من هذه يعرض <a href> حقيقيًا، فيعمل النقر بالزر الأوسط والفتح في علامة تبويب جديدة وزواحف البحث.
شريط التنقّل
split - الحجم لم يُقَس بعدترويسة مثبّتة روابطها HTML من الخادم، ويكتب جزء صغير واحد في المتصفح ارتفاعها في --tc-sticky-top.
جرّبه
الشيفرة
import { Button } from 'topclassui/button'
import { NavbarActions, NavbarBrand, NavbarLink, NavbarLinks, NavbarRoot } from 'topclassui/navbar'
<NavbarRoot>
<NavbarBrand href="/">TopClass UI</NavbarBrand>
<NavbarLinks>
<NavbarLink href="/docs">Docs</NavbarLink>
<NavbarLink href="/components">Components</NavbarLink>
<NavbarLink href="/icons">Icons</NavbarLink>
</NavbarLinks>
<NavbarActions>
<Button render={<a href="/login" />}>Sign in</Button>
</NavbarActions>
</NavbarRoot>يكتب الشريط ارتفاعه في --tc-sticky-top، فيتوقف هدف الرابط #anchor تحته لا خلفه.
كل الحالات، بدون تمرير الفأرة
حالتانالشريط الجانبي
split - الحجم لم يُقَس بعدشريط جانبي روابطه تُعرض على الخادم، وينطوي إلى عمود من الأيقونات. الشريط وزرّه وحدهما يعملان في المتصفح.
جرّبه
الشيفرة
import { folder } from 'topclassui/icons/folder'
import { gear } from 'topclassui/icons/gear'
import { house } from 'topclassui/icons/house'
import { Svg } from 'topclassui/svg'
import { SidebarBody, SidebarGroup, SidebarHeader, SidebarLink, SidebarRoot } from 'topclassui/sidebar'
<div className="h-96">
<SidebarRoot>
<SidebarHeader>Workspace</SidebarHeader>
<SidebarBody>
<SidebarGroup>
<SidebarLink href="#home" icon={<Svg icon={house} />}>Home</SidebarLink>
<SidebarLink href="#projects" icon={<Svg icon={folder} />}>Projects</SidebarLink>
<SidebarLink href="#settings" icon={<Svg icon={gear} />}>Settings</SidebarLink>
</SidebarGroup>
</SidebarBody>
</SidebarRoot>
</div>مع defaultOpen={false} يرسل الخادم الشريط مطويًّا، فلا يتحرك شيء عند الترطيب.
كل الحالات، بدون تمرير الفأرة
حالتانعلامات التبويب
split - الحجم لم يُقَس بعدعروض متعددة لشيء واحد، يظهر منها واحد في كل مرة. كل لوحة تُعرض على الخادم وتبقى في HTML، فيُفهرَس النص المخفي أيضًا.
جرّبه
الشيفرة
import { TabsList, TabsPanel, TabsRoot, TabsTab } from 'topclassui/tabs'
<TabsRoot defaultValue="overview">
<TabsList aria-label="Project">
<TabsTab value="overview">Overview</TabsTab>
<TabsTab value="activity">Activity</TabsTab>
<TabsTab value="settings">Settings</TabsTab>
</TabsList>
<TabsPanel value="overview">Revenue is up 12% against last quarter.</TabsPanel>
<TabsPanel value="activity">Three people edited this project today.</TabsPanel>
<TabsPanel value="settings">Only an owner can rename or delete this project.</TabsPanel>
</TabsRoot>keepMounted هنا true، وقيمته في Base UI هي false: اللوحات غير النشطة تبقى مخفية في HTML القادم من الخادم.
كل الحالات، بدون تمرير الفأرة
حالتانأو استورده من الحزمة
import { TabsRoot, TabsList, TabsPanel, TabsTab } from 'topclassui/tabs'ثبّت مرة واحدة:npm i topclassui@beta zod
الأكورديون
split - الحجم لم يُقَس بعدأقسام يفتحها القارئ واحدًا تلو الآخر. اللوحات المغلقة تبقى في HTML، فيقرؤها الزاحف ويفتحها البحث في الصفحة.
جرّبه
الشيفرة
import { AccordionItem, AccordionRoot } from 'topclassui/accordion'
<AccordionRoot className="max-w-105">
<AccordionItem value="server" title="What is a server component?">
<p>A component that renders on the server and sends HTML, with no JavaScript of its own.</p>
</AccordionItem>
<AccordionItem value="arabic" title="Does it work in Arabic?">
<p>Yes. The caret moves to the left edge and the text starts at the right one.</p>
</AccordionItem>
<AccordionItem value="bundle" title="How big is the bundle?">
<p>About one kilobyte in the browser. The answers cost nothing: they are plain HTML.</p>
</AccordionItem>
</AccordionRoot>اللوحة المغلقة hidden="until-found": بحث المتصفح أو رابط #id أو جزء نصي في العنوان يفتحها.
كل الحالات، بدون تمرير الفأرة
حالتانأو استورده من الحزمة
import { AccordionRoot, AccordionItem } from 'topclassui/accordion'ثبّت مرة واحدة:npm i topclassui@beta zod
مسار التنقل
server - 0 KBموقع الصفحة في الموقع، بروابط حقيقية ودون JavaScript. والمسار نفسه يُكتب BreadcrumbList بصيغة JSON-LD.
الشيفرة
import { Breadcrumb } from 'topclassui/breadcrumb'
/** The last item has no href: it is the page being read, and it stays out of the JSON-LD. */
export default function Demo() {
return (
<Breadcrumb
items={[
{ href: '/', label: 'Home' },
{ href: '/docs', label: 'Docs' },
{ label: 'Breadcrumb' },
]}
/>
)
}
اترك href عن العنصر الأخير ليصبح الصفحة الحالية. العناصر ذات الروابط تُكتب أيضًا في BreadcrumbList بصيغة JSON-LD.
كل الحالات، بدون تمرير الفأرة
3 حالاتترقيم الصفحات
server - 0 KBأرقام صفحات هي روابط حقيقية لا معالجات نقر، فتعمل دون JavaScript ويتبع الزاحف كل صفحة منها.
جرّبه
الشيفرة
import { Pagination } from 'topclassui/pagination'
<Pagination value={6} pageCount={12} getHref={(page) => `/blog?page=${String(page)}`} />getHref يبقي كل صفحة قابلة للزحف، وقيم value وsiblingCount وboundaryCount محصورة في مدى آمن.
كل الحالات، بدون تمرير الفأرة
4 حالاتأو استورده من الحزمة
import { Pagination } from 'topclassui/pagination'ثبّت مرة واحدة:npm i topclassui@beta zod