UIDex

زرّ الإجراء العائم

Floating Action Button

FloatingActionButton

اسمه كمانالزرّ العائم، فاب، زرّ الإضافة العائم، الزرّ الدائري العائم، زرّ الإجراء الأساسي، FAB، floating button، extended FAB، mini FAB، speed dial button، compose button

زرّ الإجراء العائم قرص يطفو فوق محتوى الشاشة في طبقة أعلى منه، ويبقى في مكانه بينما يمرّ المحتوى تحته، ويحمل الفعل الوحيد الأرجح أن يريده المستخدم في هذه الشاشة. وهو يختلف عن الزرّ العادي في أنه خارج تدفّق التخطيط: الزرّ العادي يجلس داخل الصفحة فيدفعه المحتوى ويرحل معه عند التمرير، أمّا هذا فمثبّت في الركن ويغطّي ما يمرّ تحته. ويختلف عن أزرار شريط التطبيق أو شريط الأدوات، لأن تلك أفعال متعدّدة متساوية في الوزن تخصّ ما هو معروض الآن (فرز، بحث، مشاركة)، بينما العائم فعل واحد رُفع فوق كل ما حوله. ويختلف عن القائمة الشعاعية (speed dial)، وهي العنقود الذي يتفرّع منه ثلاثة أو أربعة أزرار مصغّرة عند الضغط: تلك حالة موسّعة من العنصر نفسه لا عنصر مستقلّ، واللجوء إليها غالباً اعتراف بأنك لم تستطع اختيار فعل واحد. وله قاعدة تحسم أغلب النقاش في المراجعة: زرّ عائم واحد في الشاشة يحمل فعلاً ينشئ أو يضيف. أمّا الحذف وكل فعل لا رجعة فيه، ومعهما الأفعال الثانوية، فلا مكان لها في موضع يَعِد بحجمه وارتفاعه بأنه أهمّ ما في الشاشة. وإن لم يكن في الشاشة فعل يستحقّ هذا الوعد فالصواب ألّا يكون فيها زرّ عائم أصلاً.

لو قلت عليه…

«الدايرة اللي في الركن وفيها علامة زائد»«زرار الإضافة اللي فوق القايمة»«الزرار اللي واقف مكانه لمّا بنزّل الصفحة»«الزرار الطويل اللي مكتوب عليه «إنشاء» وطايف فوق الشاشة»«الزرار اللي أول ما تدوس عليه يطلّع كام زرار صغير فوقه»

العيّنة الحيّة

تفاعل مع الديمو. كل الأجزاء حقيقية ومرقّمة.

٩:٤١

الرسائل

فريق التصميمالمراجعة النهائية اليوم
سارة النعيميبعتت لك الملفّ
الدعم الفنّيتمّ إغلاق التذكرة
محمود عادلنشوفك بكرة الساعة ٩
أرشيف قديملا جديد هنا
حيّز محجوز للزرّ
RTLالزرّ يقفز إلى الركن المقابل، والزائد لا ينعكس. جرّب الفتح والتمرير

تشريح العنصر: كل جزء واسمه

مرّر على أي سطر ليتحدّد مكانه في الديمو فوق.

برومبت البناء

ابنِ زرّ إجراء عائماً لشاشة موبايل. زرّ واحد فقط في الشاشة يحمل الفعل الأرجح فيها (إنشاء أو إضافة). ولا تضع فيه حذفاً ولا أي فعل لا رجعة فيه، وأبعد عنه الأفعال الثانوية. اجعله قرصاً ٥٦dp بأيقونة ٢٤dp، وثبّته بـ inset-inline-end و inset-block-end فوق env(safe-area-inset-bottom) مع viewport-fit=cover في وسم viewport، ولا تكتب right ولا left ولا bottom في أي قاعدة تخصّ موضعه. الأيقونة بلا نصّ مصاحب، فأعطِ الزرّ اسماً مقروءاً يصف الفعل عبر aria-label على الويب و contentDescription على أندرويد و accessibilityLabel على iOS، ولا تسمّه باسم الشكل. احجز في نهاية القائمة التي تحته حيّزاً يساوي ارتفاعه زائد هامشيه (نحو ٨٨dp) عبر contentPadding حتى يبقى العنصر الأخير قابلاً للّمس، وارفع الزرّ فوق شريط الإشعار السفلي عند ظهوره بتغيير inset-block-end بدل أن يتغطّى به. أضف نسخة ممتدّة: كبسولة فيها الأيقونة عند البداية المنطقية ونصّ من كلمة أو كلمتين لا يُقصّ ولا يلتفّ، تنكمش إلى دائرة عند التمرير لأسفل وتعود عند العودة إلى الأعلى، وافصل بين الأيقونة والنصّ بـ gap لا بـ margin-right، ولا تضف row-reverse داخل [dir="rtl"]. وإن احتجت قائمة شعاعية فاجعل الزرّ الأصل عنصر تبديل يحمل aria-expanded، وأعطِ كل زرّ مصغّر (٤٠dp مرسوماً و٤٨dp هدف لمس) تسمية مرئية بجانبه، وافتح العنقود إلى الأعلى بـ flex-direction: column من غير أي قلب في RTL، وأغلقه بالضغط خارجه وبمفتاح Escape ولا تحبس التركيز داخله. اضبط مصدر حركة الانبثاق بـ transform-origin تقلبها يدوياً داخل [dir="rtl"] لأن transform لا مكافئ منطقياً لها. ولا تعكس أيقونة الزائد ولا الكاميرا في العربية: اقصر أي قلب على الأيقونات الاتجاهية وحدها.

سلوكه في الاتجاه من اليمين لليسار

الجزء ده حصري عندنا.

الركن ينتقل إلى الجهة المقابلة

ينعكس

الزرّ المثبّت في الركن السفلي الأيمن في الإنجليزية يجلس في الركن السفلي الأيسر في العربية، لأن مرساته نهاية سطر القراءة لا جهة بعينها. على الويب اكتب inset-inline-end مع inset-block-end بدل right و bottom. وفي Compose استعمل Modifier.align(Alignment.BottomEnd) أو FabPosition.End داخل Scaffold، وابتعد عن AbsoluteAlignment.BottomRight فهي تتجاهل LayoutDirection عن قصد. وفي تخطيطات أندرويد التقليدية القيمة android:layout_gravity="bottom|end" لا "bottom|right"، ولن ينعكس شيء منها على الجهاز ما لم تكن android:supportsRtl="true" موضوعة على وسم <application>. وعلى iOS قيّد الزرّ بـ trailingAnchor لا rightAnchor، أو ضعه بـ ‎.overlay(alignment: .bottomTrailing)‎ في SwiftUI.

رمز الزرّ لا ينعكس

لا ينعكس

علامة الزائد والكاميرا والقلم والميكروفون أشكال ثابتة لا اتجاه في معناها، وقلبها في العربية يجعلها تبدو معطوبة لا مترجمة. الذي ينعكس هو الأيقونة الاتجاهية وحدها: سهم الرجوع، سهم الإرسال، علامة «التالي». لذلك لا تكتب قاعدة شاملة مثل [dir="rtl"] svg { transform: scaleX(-1) }، بل اقصر القلب على صنف تضعه على الأسهم. والمنصّات تحمل هذه المعلومة بنفسها: android:autoMirrored="true" على الـ vector drawable، و Icons.AutoMirrored.Filled.Send في Compose، و ‎Image.flipsForRightToLeftLayoutDirection(true)‎ في SwiftUI، وفي SF Symbols اختر arrow.backward التي تنعكس بدل arrow.left التي لا تنعكس أبداً. أمّا الزائد الذي يدور ٤٥ درجة ليصير علامة إغلاق فلا يحتاج شيئاً من هذا، لأن الدوران محايد تجاه الاتجاه.

الأيقونة عند بداية الكبسولة الممتدّة

ينعكس

داخل الكبسولة تقع الأيقونة عند البداية المنطقية والنصّ بعدها: يمين النصّ في العربية، يساره في الإنجليزية. اجعل الكبسولة صفّ flex واترك ترتيب DOM كما هو، فالمحور المضمّن ينعكس وحده. ولا تضف flex-direction: row-reverse داخل [dir="rtl"]، لأنك تقلب ما انقلب فتعود الأيقونة إلى الجهة الخطأ وينفصل الترتيب البصري عن ترتيب القراءة وعن ترتيب التنقّل. والمسافة بين الأيقونة والنصّ تُكتب gap أو margin-inline-end لا margin-right. وخانة icon في ExtendedFloatingActionButton داخل Compose تضعها عند البداية تلقائياً، و Label في SwiftUI يفعل الشيء نفسه. أمّا حشو الكبسولة غير المتماثل (أضيق عند الأيقونة وأوسع عند النصّ) فمكانه padding-inline-start و padding-inline-end.

محور العنقود لا ينقلب

لا ينعكس

العنقود يفتح إلى الأعلى في الاتجاهين معاً، لأنه يترتّب على المحور الكتلي، و direction لا تمسّ هذا المحور أصلاً (الذي يقلبه writing-mode وحده). فحاوية flex-direction: column لا تحتاج column-reverse في العربية، ويبقى ترتيب DOM مطابقاً للترتيب البصري ولترتيب قارئ الشاشة، وعكس المصفوفة باليد هو ما يفسد هذا التطابق. أمّا تسمية كل زرّ مصغّر فتقع عند بدايته المنطقية: يساره في الإنجليزية ويمينه في العربية. ضع التسمية قبل الزرّ في DOM داخل صفّ flex فتنعكس مجّاناً، أو ثبّتها بـ inset-inline-end: calc(100% + 8px) لا بـ right. وحاذِ العنقود كلّه بـ align-items: flex-end، فهي في حاوية عمودية محاذاة على المحور المضمّن تتبع direction من تلقاء نفسها.

مصدر حركة الانبثاق يُقلب يدوياً

ينعكس

حين تكبر الكبسولة أو ينبثق العنقود من القرص تبدأ الحركة من ركن الزرّ، وهذا الركن ينتقل مع الاتجاه بينما transform لا تعرف الاتجاه: لا مكافئ منطقياً لها في CSS، و transform-origin على المحور الأفقي لا تقبل إلا left أو right أو center أو طولاً أو نسبة مئوية، ولا وجود لكلمة inline-end فيها. اكتب transform-origin: bottom right ثم اقلبها إلى bottom left داخل [dir="rtl"]، أو احسب النسبة من متغيّر اتجاه. وفي المقابل الارتفاع فوق شريط الإشعار السفلي حركة على المحور الكتلي: قيمة translateY أو inset-block-end واحدة في الاتجاهين ولا تحتاج قاعدة RTL إطلاقاً، والعطب المعتاد هناك ليس الاتجاه بل ترك الشريط يغطّي الزرّ لأن الاثنين مثبّتان في الركن نفسه بلا تنسيق بينهما. وفي أندرويد يتكفّل CoordinatorLayout بهذه الإزاحة عبر FloatingActionButton.Behavior، وخارجه ترفعه بنفسك.

أسماؤه في الكود

كل سطر هو كلمة مكتبة واحدة عن نفس الشيء. خد السطر اللي بيكلّم مشروعك.

Jetpack ComposeScaffold(floatingActionButton = { FloatingActionButton(…) })خانة Scaffold تتولّى الموضع وحشوات النوافذ؛ و FabPosition.End تجعل الركن يتبع LayoutDirection.
Material ComponentsFloatingActionButton.Behavior + CoordinatorLayoutداخل CoordinatorLayout ترفع هذه الـ Behavior الزرّ فوق الـ Snackbar بضبط dodgeInsetEdges على bottom؛ خارجه ترفعه بنفسك.
SwiftUI.overlay(alignment: .bottomTrailing) { … }لا يوجد زرّ عائم جاهز في iOS، فتركّبه بنفسك، و‎.bottomTrailing هي ما ينتقل مع layoutDirection.
UIKitview.trailingAnchor / safeAreaLayoutGuideمرساة trailing تُحلّ حسب اتجاه التخطيط بينما rightAnchor لا تفعل، و safeAreaLayoutGuide هي ما يبعده عن مؤشّر الشاشة.
FlutterfloatingActionButtonLocation: FloatingActionButtonLocation.endFloatالأسماء منطقية (endFloat و endDocked و startFloat) ولا وجود لـ rightFloat أصلاً.
HTML<button aria-label="…" aria-expanded>لا يوجد role مخصّص لهذا العنصر على الويب: زرّ عادي بموضع ثابت، و aria-expanded تُضاف فقط عند وجود قائمة شعاعية.
CSSinset-inline-end + inset-block-endالزوج المنطقي الذي ينقل الزرّ إلى الركن المقابل وحده عند انقلاب الصفحة.

شوف كمان

آخر تحديث · 2026-08-19