Switch / Toggle
مفتاح التبديلrole="switch"
also calledtoggle، toggle switch، on/off switch، settings toggle، switch control، slider toggle، سويتش، توجل، مفتاح تشغيل وإيقاف، مفتاح الإعدادات، زرّ التفعيل، مفتاح منزلق
A switch is a two-state control, on or off, that applies its change the instant the user presses it, with no save step and no confirmation. It belongs on settings screens and anywhere a choice takes effect immediately: dark mode, notifications, background sync. It is not a checkbox: a checkbox is a value gathered with the rest of a form and sent when you press Save, so there is still room to change your mind before submitting, while a switch leaves nothing to reconsider because the change already landed. It is not a toggle button either: a toggle button carries aria-pressed and reports the state of a tool inside a toolbar, such as Bold in an editor, whereas a switch carries role="switch" with aria-checked and reports a setting that stands on its own. And it is not a pair of radio buttons, which offer one choice out of several named alternatives, where a switch offers only a thing and its opposite. One rule settles the argument in review: if the screen needs an Apply button next to the switch, you did not want a switch, you wanted a checkbox inside a form.
If you called it…
Live specimen
Interact with the demo. Every part is real and numbered.
الإعدادات
كل مفتاح يُطبَّق لحظة الضغط، ولا يوجد زرّ «حفظ» في هذه الشاشة.
Anatomy: every part, named
Hover a row to locate it in the demo above.
Build prompt
How it behaves right-to-left
This section is ours alone.
Which way the thumb travels
mirrorsThe thumb always travels toward the logical end of the track, which means the ON position sits on the LEFT in Arabic after sitting on the right in English. The problem is that transform is a purely physical property with no logical counterpart anywhere in CSS, so translateX(22px) shoves the thumb clean out of the track in RTL. Write it as translateX(calc(22px * var(--dir))) with --dir set to -1 under [dir="rtl"], or drop the translation altogether by making the track a flex container and swapping justify-content between flex-start and flex-end, since the inline axis follows direction on its own. That version cannot be animated, so use the direction variable when you want a smooth transition.
How the track fills
mirrorsA track that fills with colour behind the thumb has to grow from the logical start, which in Arabic is the right. Build the fill as an inner element pinned with inset-inline-start: 0 and animate its inline-size from 0 to 100%: it mirrors on its own, because the edge it is anchored to is the one that moved. transform: scaleX() will not do here, because transform-origin accepts no logical keyword at all, on the horizontal axis it takes left, right, center, a length or a percentage and nothing else , so you end up hand-writing transform-origin: right under [dir="rtl"]. Same for a two-tone track painted with linear-gradient(to right, …): gradients have no logical keywords either, so flip it to to left.
Where the label sits
mirrorsIn a settings row the label sits at the logical start and the control at the logical end, so the whole row swaps in Arabic: text on the right, switch on the left. Use a flex row with justify-content: space-between, or margin-inline-start: auto on the control. Never float: right and never margin-left: auto. And do not add flex-direction: row-reverse under [dir="rtl"] thinking you are fixing the direction: the inline axis already flipped once, the second flip puts the control back on the wrong side and leaves the visual order disagreeing with DOM order and with Tab order.
The glyphs inside the track
never mirrorsThe two glyph positions swap with the track by themselves, because each one is pinned to a logical edge, but the glyphs must not be flipped: the Bidi_Mirrored property of the check character is No, and its meaning does not change with page direction. A blanket [dir="rtl"] svg { transform: scaleX(-1) } therefore breaks the checkmark, and breaks the spinner inside the thumb along with it, leaving both looking drawn back to front. Scope the flip to a class you put on arrows only.
The drag gesture
mirrorsIf your switch can be dragged instead of tapped, then in Arabic it is a drag toward the LEFT that means "turn it on". Pointer events hand you a purely physical delta: movementX (or current clientX minus the start) is positive for rightward motion whatever the page direction, so a plain dx > threshold inverts the whole gesture in Arabic. Multiply the delta by a direction sign read from getComputedStyle(el).direction === "rtl" ? -1 : 1 before comparing, and put touch-action: pan-y on the track so page scrolling does not swallow the gesture.
In code
Each row is one framework's word for the same thing. Take the row your project speaks.
See also
Updated · 2026-08-19