Prefix and suffix placement in RTL inputs
Place prefixes, units and input buttons in RTL forms by separating value direction, component layout, logical spacing and submitted data.
By Omran Khleifat, Founder of Ritla
Guide13 min read
An Arabic input with a unit or currency sign has two ordering problems: where the extra element belongs, and how the editable value reads. Changing the page to RTL can move a unit to the intended side while leaving the input's reserved space on the other side. The next digit then appears underneath the unit.
“Put every prefix on the right” is not a sufficient rule. A fixed prefix belonging to an LTR identifier still precedes that identifier in its LTR sequence. A search icon can belong at the Arabic component's inline start. A currency symbol follows the product's locale-specific amount pattern. These are different relationships despite sharing one component API.
This guide uses a parcel quotation form: package weight, a dispatch-account reference and a declared-value amount. The Arabic labels belong to the interface. The numeric values, fixed reference fragments and interactive buttons need their own placement and data rules.
Decide what prefix and suffix mean in this component#
Component libraries use those words for several things. Before changing CSS, identify which relationship the product needs.
| Extra element | What determines its position | What it contributes |
|---|---|---|
| Arabic weight unit | The chosen Arabic measurement layout | Meaning for the number |
| Fixed dispatch-reference prefix | The identifier's defined sequence | A non-editable part of a reference |
| Currency symbol or code | The product's locale and amount convention | Currency context |
| Search icon | The component's interface layout | A visual clue |
| Clear button | The component's action layout | An operation on the editable value |
A library's prefix slot may mean “before the input in DOM order,” “at inline start,” or “at the physical left.” Those meanings coincide in an English demo and diverge in an Arabic form. Inspect the rendered component and its documentation instead of relying on the prop name.
Write the intended relationship in plain language. For the Arabic weight field here, the reader meets the number from the right, then its Arabic unit to the left. The dispatch reference, by contrast, is one LTR sequence with a fixed beginning. That sequence should remain intact inside the Arabic page.
Make those product decisions before choosing logical properties. Logical CSS maps a declared relationship to an edge; it cannot decide which relationship is correct.
Trace the extra element through the library's slots#
A design-system input may render an outer field, a border wrapper, an input, two adornment wrappers and a validation message. A direction prop on the outer component may reach only some of those elements. Inspect the actual DOM before assuming the library passes it to the element that owns layout.
Temporarily outline the input and adornment wrappers in DevTools. Find whether the extra text occupies a flex item, an absolutely positioned slot or a pseudo-element. Then identify the declaration that reserves its width. This separates a wrong-side defect from a width defect: a correctly positioned unit can still cover text if its wrapper contributes no space.
Read the library's direction and slot contracts before adding overrides. If its layout already reverses under RTL, another reversal in application CSS can undo that behavior. If it explicitly forces the editable input LTR, that can be intentional for the value while still requiring different spacing around an Arabic unit.
Check semantic direction and computed CSS separately. A library that changes only CSS direction can look RTL while a selector based on :dir(rtl) does not match the same element. The supplied browser measurements distinguish these cases. Apply overrides at the intended direction boundary rather than adding increasingly broad selectors until the current screenshot changes.
Separate the page, the group and the editable value#
An Arabic form can have RTL labels, an RTL measurement group and an LTR numeric input inside that group. These directions do not contradict one another. They apply to different elements.
The page direction supplies the surrounding reading order. The group direction controls the order of the input and its adjacent elements. The input direction controls its editable text and the resolution of logical properties applied to that input. MDN's dir reference explains direction inheritance and explicit boundaries.
Changing dir on the input does not reorder its sibling unit. Changing the wrapper's direction can reorder a flex row while the input remains explicitly LTR. This distinction explains why a text-direction fix can leave an adornment unchanged.
For this guide's weight fixture, the product uses the Western-digit decimal string 7.35. Its field explicitly uses LTR editing. That is a policy for this value, not a recommendation to make every Arabic text input LTR. An Arabic parcel-description field has different content and should retain the appropriate Arabic editing direction.
The supplied layout and bidi measurements were made in Chromium 151 on September 24, 2026; Firefox and Safari were not measured. Use the same distinction between layout and value direction when testing your supported browsers. For the broader inheritance rules, see nested LTR content inside an RTL page.
Watch for logical padding resolved in the wrong direction#
This weight control attempts to overlay the Arabic unit at the group's inline end and reserve space with the input's inline-end padding:
<div class="weight-overlay" lang="ar" dir="rtl"> <label for="overlay-mass">وزن الطرد</label> <div class="weight-overlay__box"> <input id="overlay-mass" name="mass" type="text" dir="ltr" inputmode="decimal" value="7.35" aria-describedby="overlay-unit"> <span id="overlay-unit" class="weight-overlay__unit">كغ</span> </div></div>/* Problem: the unit and input resolve "end" differently. */.weight-overlay__box { position: relative;} .weight-overlay__unit { position: absolute; inset-inline-end: 0.8rem; inset-block-start: 0;} .weight-overlay__box input { padding-inline-end: 3.75rem;}In horizontal layout, the unit inherits RTL and its inline end is left. The input is LTR, so its inline-end padding is right. The declarations contain the same word but reserve different physical sides. Whether the current value visibly collides depends on sizing and alignment; the mismatched reservation exists before that collision becomes visible.
This follows from the mappings documented for inset-inline-end and padding-inline. Resolve each declaration using the element it is applied to, not the nearest Arabic ancestor.
Increasing the padding number will not repair the relationship. Neither will changing only text-align. Prefer a layout where the unit occupies real space beside the input. If an overlay is required, calculate both the unit position and the reserved space from the same intended physical relationship, including any direction difference between their elements.
Put a unit in normal flow when it can live there#
For the same weight field, replace the overlay with a group that allocates space to the input and the unit separately:
<div lang="ar" dir="rtl"> <label for="parcel-mass">وزن الطرد</label> <div class="affixed-control" dir="rtl"> <input id="parcel-mass" name="mass" type="text" dir="ltr" inputmode="decimal" value="7.35" aria-describedby="mass-unit mass-help"> <span id="mass-unit" class="affixed-control__extra">كغ</span> </div> <p id="mass-help">أدخل الوزن بالكيلوغرام دون كتابة الوحدة.</p></div>.affixed-control { display: flex; align-items: center; gap: 0.65rem; max-inline-size: 28rem; box-sizing: border-box; padding: 0.6rem 0.8rem; border: 1px solid currentColor;} .affixed-control > input { flex: 1; inline-size: 0; min-inline-size: 0; font: inherit; padding-block: 0.25rem; padding-inline: 0.35rem;} .affixed-control__extra { flex: none;}The RTL group lays out the input first from the right, followed by the Arabic unit toward the left. The input's own LTR direction governs its text. The unit consumes its actual width, so the input no longer needs a guessed overlay allowance. MDN's flex-direction reference describes how row direction follows the flex container.
The input can shrink because its minimum inline size is zero. Test the resulting editing area with long values; shrinkability is not permission to make it unusably narrow. Keep a visible focus indicator on the real input. A surrounding border does not replace the need to identify which element has keyboard focus.
The unit remains a sibling, outside the editable string. The help text tells the person to enter the number alone. This is a layout and labelling example, not a complete decimal parser: inputmode="decimal" requests a suitable input interface but does not validate or normalize decimal text.
Keep a fixed LTR prefix with its LTR reference#
The quotation form also accepts a dispatch account whose reference begins with CARGO/. The user edits the remaining part, such as 6WQ8. The product defines that reference as an LTR sequence.
Use the same group styles, but give the reference group its own direction and place the fixed part before the input:
<div lang="ar" dir="rtl"> <label for="dispatch-account">مرجع حساب الشحن</label> <div class="affixed-control" dir="ltr"> <span class="affixed-control__extra" aria-hidden="true">CARGO/</span> <input id="dispatch-account" name="accountSuffix" type="text" dir="ltr" value="6WQ8" aria-describedby="account-help"> </div> <p id="account-help"> أدخل الجزء الذي يلي <bdi dir="ltr">CARGO/</bdi> فقط. </p></div>The Arabic label stays outside the LTR sequence. The prefix appears before the editable remainder within that sequence; mirroring it to the opposite side because the page is Arabic would change the reference's defined order.
Here the fixed part is hidden from assistive technology as a visual fragment, while the label and help explain the editing task. If people need the full prefix to understand or verify the reference, include that information in the accessible instructions too. Do not hide meaningful information without an equivalent explanation.
Do not use this approach for a field intended to accept the full reference from a document. That field should let someone paste the full reference. Splitting it into a fixed visual fragment and an editable remainder changes what paste, selection and submission mean; it needs a product reason beyond appearance.
Treat currency placement as a formatting decision#
A declared-value field needs to make its currency clear, but “Arabic page” does not settle where every currency symbol belongs. Agree on the market, currency, digit style and amount pattern. Then decide how much of that pattern belongs to the editor and how much belongs to a formatted display.
An amount editor can use a labelled numeric value with a separate currency indicator. A receipt-style formatted amount can contain spacing and directional formatting that should not become editable characters by accident. Do not populate the editor with a formatted display string and then attempt to remove every unfamiliar character on each keystroke.
If a component library puts a currency symbol into its prefix slot, verify what that slot means for this particular amount pattern. A generic mirror switch can move the symbol without changing the pattern the product intended. Conversely, retaining an English physical-side rule can be wrong for the chosen Arabic pattern.
Specify currency context in the label or associated instructions as well as the decoration. The user should still understand the amount if a decorative symbol is unavailable or its pronunciation is unclear. A nearby visual code such as EUR is not automatically part of the input's accessible name or submitted value.
Keep currency formatting and decimal parsing as separate responsibilities. This page's placement fixes do not establish which separators or digit forms the quotation service accepts. Make that acceptance policy visible and test it with the amount field rather than inferring it from the symbol's side.
Keep adornments out of values unless they are data#
An adjacent span is not part of input.value. That is useful for the weight field: selecting its contents should select the editable number, and clearing it should leave the unit available as context.
Putting the unit directly into the value changes the task. The person can delete it, paste it twice or move the caret into it. A numeric parser then has to distinguish the measurement from the extra text. Preventing edits around the unit adds selection and keyboard rules that the sibling-element version does not need.
Inspect the actual form payload with FormData or the application's request inspector. For the weight fixture, establish whether the server receives the numeric string and a separate unit identifier, or whether the unit is fixed by the field contract. A visible unit does not automatically submit itself.
The dispatch-account example submits only accountSuffix. Its receiving code must know the fixed prefix and validate the complete reference it represents. If your endpoint expects the full reference instead, assemble that value deliberately before submission or use a full-reference field. CSS cannot change the request contract.
Test autofill and paste against that contract. Pasting an entire measurement into a number-only editor needs an explicit acceptance or rejection policy. Do not silently strip arbitrary letters, because accepting a value after deleting its unit can misinterpret what the person intended.
Define copying and pasting for split references#
The dispatch-account editor visually presents a complete reference, but only its remainder is editable. Someone copying from an existing document may therefore paste CARGO/6WQ8 into a field that expects 6WQ8. The adjacent prefix then appears twice. This is a mismatch between the editing contract and the source material, not a bidi rendering failure.
Decide whether the editor accepts the full reference as an alternative paste format. If it does, recognize the specified prefix at the start and validate the remaining reference under the same rules used for typed content. Do not remove matching fragments from arbitrary positions or guess which characters are decoration. An unexpected prefix should produce an explanation, not a different account reference.
The simpler choice can be a full-reference editor with no fixed prefix decoration. Keep the split control only when it makes the task clearer for its users. A component that saves a few typed characters but rejects the reference people actually possess is making the wrong tradeoff.
Copying needs its own decision. Select-all inside the input selects the editable remainder, not the adjacent span. If the workflow needs a complete-reference copy action, construct it from the current validated reference model and label that action clearly. Avoid silently changing ordinary copy behavior while the person is editing a selection.
Use separate fixtures for typing the remainder, pasting the complete reference and copying a corrected value. Verify the resulting string, visible prefix and submitted reference together. Passing only the typing case leaves the split-field contract untested.
Make units and instructions available without relying on position#
A visible suffix does not automatically describe an input to assistive technology. Use a real label and associate any extra instructions or unit information that the editing task needs.
The weight example references both mass-unit and mass-help through aria-describedby. W3C's form instruction guidance explains this association, and MDN's aria-describedby reference distinguishes a description from the control's name. Check the resulting announcement with the assistive technology combinations you support; repeated unit information may need editorial adjustment.
Do not replace the visible Arabic label with a unit-only accessible name. The person needs to know what is being measured, not merely which unit is used. Keep required-field feedback, help and the unit associated when errors appear, rather than overwriting one description ID with another.
Avoid using CSS-generated content as the only source of a meaningful prefix or unit. MDN's ::after reference cautions against relying on generated content for screen-reader access. A real element with an explicit association is easier to inspect, translate and update.
Keep the label association pointed at the actual input. Clicking the label should target that input, not merely paint a focus border around the wrapper. If the component intercepts clicks on empty wrapper space, test that behavior without preventing its buttons from receiving their own clicks.
Treat clear buttons and unit selectors as controls#
A search icon and a clear button may occupy similar visual slots, but only one performs an action. A decorative icon can be hidden from assistive technology. A clear button needs a name, keyboard access and a defined result.
For the weight example, this optional button can be placed after the field's help text:
<button id="clear-mass" type="button" lang="ar" dir="rtl"> مسح الوزن</button>const massInput = document.querySelector("#parcel-mass");const clearMass = document.querySelector("#clear-mass"); clearMass.addEventListener("click", () => { if (massInput.disabled || massInput.readOnly) return; massInput.value = ""; massInput.dispatchEvent(new Event("input", { bubbles: true })); massInput.focus();});This plain-DOM example clears the editable number and returns focus to the input; the adjacent unit remains. It explicitly dispatches an input event because programmatic value assignment does not generate one, as MDN's input event reference explains. In a controlled framework component, use its state-update API instead of assuming this DOM assignment updates framework state.
Synchronize the button's disabled state with the field as well. The handler's guard protects the value, but an apparently available button that does nothing still needs a presentation decision. type="button" also prevents this action from unintentionally submitting the form.
A unit selector is another real control, not a decorative suffix. Give it a visible or accessible label and a keyboard position that fits the task. Changing its selected unit must not silently reinterpret the existing weight. Decide whether a change converts the value, clears it with explanation or requests a new entry.
Preserve meaning when space or language changes#
Normal flow removes overlay guesses, but a long translated unit or a new action button can still consume the editing area. Test the narrowest supported form, increased text size and the actual Arabic font. Do not conceal a meaningful unit with overflow: hidden merely to keep the group's outline tidy.
If the group no longer fits, move supplementary instructions outside it or redesign how the unit is presented. Keep a usable editing width and a clear association between the value and its unit. A field that technically shrinks to a few characters is not enough for someone checking a pasted number.
Language switching needs separate decisions for labels and data. The Arabic label can change while the dispatch reference remains the same LTR sequence. A translated unit label should continue to represent the same stored unit. Recreating the input solely to move an adornment can lose focus, selection or an unfinished edit, so verify the transition with the field active.
Do not add row-reverse to compensate for an unexpected slot order without checking the wrapper's direction first. Preserve a source order that represents the chosen sequence, particularly when several adjacent elements are interactive. Tab order should not become a puzzle introduced by a visual reversal.
Test the relationship, not just the side#
For a failing component, inspect three elements independently: the group that owns the layout, the input that owns the editable value and the element that owns the adornment. Record their computed direction and which element receives each padding or inset declaration. This identifies the mixed-direction padding error without guessing from the screenshot.
Then exercise these states in the actual quotation form:
| State | What to verify |
|---|---|
| Empty weight | Unit and label remain clear without a value |
| Long editable number | Text stays clear of the unit and remains editable |
| Select all, copy and paste | The editable contents match the intended data contract |
| Clear with keyboard activation | The value clears, context remains and focus is usable |
| Disabled or read-only field | Adjacent actions follow the defined editing policy |
| Change unit | The value is converted or re-entered according to the stated rule |
| Switch interface language | Reference order and stored measurement meaning remain correct |
| Submit and restore saved data | The displayed adornment agrees with the saved value and unit |
Check visual placement and the payload separately. Correct submission does not prove the affix is displayed correctly, and a correct screenshot does not prove the service receives the intended measurement. Include a supported-browser check with touch input too; the decimal keyboard hint is not a guarantee that every keyboard exposes the same keys.
A free Ritla scan can report : Direction-sensitive physical properties without an RTL override, and : Visible text-bearing elements overlapping. For a unit that still collides after logical positioning, padding-inline for RTL layouts helps trace which element is reserving the space.