inset-inline-start vs left

Compare inset-inline-start with left, migrate positioned UI without changing LTR, and avoid shorthand, cascade, transform and overflow traps.

Guide14 min read

An absolutely positioned label can sit neatly in the top-left corner of an English card and remain there, equally neatly and completely wrongly, on the Arabic card. The browser has followed left. The design meant “start.” These are different instructions.

left measures from the physical left edge. inset-inline-start measures from the start edge of the element's inline writing direction. In a horizontal LTR interface, both map to the left. In a horizontal RTL interface, inline start maps to the right while left stays left.

Use the logical property when the positioned object belongs to the reading start of a component. Keep left when the object belongs to an actual coordinate on the left, such as a chart axis or map overlay. The property cannot decide which meaning the design intended. It has enough responsibilities already.

The difference in one table#

For the horizontal writing mode used by ordinary English and Arabic interfaces:

DeclarationLTR resultRTL resultMeaning
left: 1rem1rem from the left1rem from the leftPhysical left edge
right: 1rem1rem from the right1rem from the rightPhysical right edge
inset-inline-start: 1rem1rem from the left1rem from the rightInline start edge
inset-inline-end: 1rem1rem from the right1rem from the leftInline end edge

The mapping also depends on writing-mode and text orientation. In a vertical writing mode, inset-inline-start can correspond to top or bottom. “It becomes right in RTL” is accurate for horizontal Arabic UI, not the definition of the property.

The MDN reference for inset-inline-start defines that mapping and notes that the property affects positioned elements only. The CSS Positioned Layout specification defines how physical and flow-relative insets participate in relative, absolute, fixed and sticky positioning.

One detail is easy to miss: an inset describes a distance inward from an edge. With absolute positioning, left: 1rem places the box's left margin edge 1rem from the containing block's left edge. inset-inline-start: 1rem does the same from logical start. A positive relative inset behaves differently because it shifts the box away from that start edge. The same property name participates in several positioning models. CSS has never refused a second job.

Put the collection status at reading start#

A museum collection card shows a conservation status in its start-side corner. In English that is top-left. In Arabic it should be top-right.

Problem

html
<article class="collection-card">
  <span class="collection-card__status">Conservation review</span>
  <h2 class="collection-card__title">Ceramic vessel</h2>
  <p class="collection-card__summary">Gallery display paused pending inspection.</p>
</article>
css
.collection-card {
  position: relative;
  padding: 3.5rem 1.25rem 1.25rem;
}

.collection-card__status {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
}

The card establishes the containing block with position: relative. The status is taken out of normal flow and placed 0.75rem from the physical top and left edges. Changing the card to RTL does not change either physical property.

Better

html
<article class="collection-card">
  <span class="collection-card__status">Conservation review</span>
  <h2 class="collection-card__title">Ceramic vessel</h2>
  <p class="collection-card__summary">Gallery display paused pending inspection.</p>
</article>
css
.collection-card {
  position: relative;
  padding: 3.5rem 1.25rem 1.25rem;
}

.collection-card__status {
  position: absolute;
  inset-block-start: 0.75rem;
  inset-inline-start: 0.75rem;
}

The LTR geometry is unchanged: block start is top and inline start is left. In horizontal RTL, block start remains top while inline start becomes right. The status follows the component's reading start with no direction-specific override.

This is a safe conversion because the original left position represented start in the LTR design. If the badge had been at right: 0.75rem and represented trailing status, the correct replacement would be inset-inline-end, not inset-inline-start. Replacing every right with start would move the English version before Arabic even enters the room.

Positioning mode changes what an inset does#

left and inset-inline-start have no effect when position computes to static. For positioned elements, their interpretation depends on the positioning scheme.

position valueWhat the inset controls
relativeA visual offset from the box's normal position
absoluteA distance from the absolute-position containing block
fixedA distance from the fixed-position containing block, often the viewport
stickyA threshold from the relevant scrollport edge

With position: relative, the box keeps its place in normal flow. A positive inset-inline-start shifts it away from inline start, toward inline end, without moving neighboring boxes out of the way. That makes relative offsets poor tools for ordinary spacing. They can create overlap while the layout continues to reserve the original space, a small illusion with administrative consequences.

With position: absolute, the box leaves normal flow and uses its containing block. With position: fixed, the containing block is often the layout viewport, but transforms, containment and related properties on ancestors can establish a different fixed-position containing block. With position: sticky, the logical inset defines the start-side threshold at which the element is kept within its scroll container.

MDN's position reference summarizes these modes. When a logical inset appears to do nothing, inspect position first. Debating left versus start on a static element is a very pure form of procrastination.

Find the containing block before changing the offset#

An offset can be logically correct and still position the element relative to the wrong box.

For an absolutely positioned element, the containing block is established by an ancestor that creates an absolute-positioning containing block. A positioned ancestor is the familiar case:

css
.collection-card {
  position: relative;
}

.collection-card__status {
  position: absolute;
  inset-block-start: 0.75rem;
  inset-inline-start: 0.75rem;
}

Without the positioned card, the status may use a more distant ancestor or the initial containing block. It can appear attached to the page rather than the card. Switching left to inset-inline-start will move the wandering status to the other side of the wrong box. This is technically progress only if measured by travel.

The positioned-layout specification also lists properties such as transform, will-change and contain as possible causes of a containing block. When the nearest position: relative ancestor does not seem to explain the coordinates, inspect every ancestor in DevTools. A transformed wrapper can change where an absolute or fixed descendant is anchored.

Ask two separate questions:

  1. Which box should supply the coordinate system?
  2. Which logical or physical edge of that box should supply the offset?

The containing block answers the first. left, right, inset-inline-start or inset-inline-end answers the second. Mixing them into one vague “positioning bug” makes both harder to diagnose.

Translate the intent, not the old property name#

Before replacing a physical inset, describe the position without using left or right.

  • “Start corner of the card” becomes inset-inline-start.
  • “End-side dismiss action” becomes inset-inline-end.
  • “Top of the component” becomes inset-block-start.
  • “Bottom of the component” becomes inset-block-end.
  • “At x-coordinate 24 on a floor plan” remains a physical coordinate.
  • “Centered on the viewport” may not need a directional property at all.

The conversion must preserve the existing LTR result. A rule written as right: 1rem maps to inset-inline-end: 1rem when right means trailing edge in LTR. Converting it to inset-inline-start changes the English layout from right to left, which is not migration. It is a fresh and unrelated bug.

The same review applies to paired declarations. A top-end control often has top plus right; the flow-relative pair is inset-block-start plus inset-inline-end. Only the inline side changes between horizontal LTR and RTL, but using logical names for both axes can make the intent easier to read.

For broader choices about which UI should mirror, see what should be mirrored in RTL. Directional placement follows reading flow. Maps, media, charts and other coordinate systems may have different rules.

The inset-inline shorthand sets both sides#

inset-inline is shorthand for inset-inline-start and inset-inline-end. One value applies to both sides. Two values mean start, then end.

css
.collection-card__status {
  position: absolute;
  inset-inline: 0.75rem auto;
}

This places the status 0.75rem from inline start and leaves inline end unconstrained. It is equivalent to:

css
.collection-card__status {
  position: absolute;
  inset-inline-start: 0.75rem;
  inset-inline-end: auto;
}

The second value is not physical right. It is logical end, so the physical mapping changes with direction.

Be careful with one value:

css
.collection-card__status {
  position: absolute;
  inset-inline: 0.75rem;
}

That does not mean “place this at inline start.” It sets both start and end to 0.75rem. An absolutely positioned, non-replaced box with automatic inline size can stretch to fill the available space between those insets. This can be useful for a full-width banner. It is a poor surprise for a compact status label.

Use the longhand when only one edge matters. Use inset-inline: start-value auto when the explicit two-sided form improves a component API. Use equal non-auto values when stretching or constraining both sides is the actual design.

The four-value inset shorthand is physical by default. Like margin, it assigns top, right, bottom and left:

css
.overlay {
  inset: 1rem 2rem 3rem 4rem;
}

Those inline sides do not switch in RTL. MDN's logical positioning guide distinguishes inset-inline and inset-block from the physically mapped inset shorthand.

A fallback can pin both sides#

MDN marks inset-inline-start and inset-inline as broadly available since April 2021. Check that against the browsers your product supports before adding compatibility code.

This fallback pattern looks harmless:

css
.collection-card__status {
  left: 0.75rem;
  inset-inline-start: 0.75rem;
}

In LTR, both declarations map to the left side, and the later logical declaration wins there. In RTL, they address different sides: physical left remains left while inline start maps to right. The absolute box now has both inline insets set.

If its inline size is automatic, the box can stretch between those sides. A tiny status label can become a bar across the card. If it has an explicit size, the positioning becomes overconstrained and one inset must yield according to the positioning rules. Either result is a poor fallback for “put it near start.”

If an older browser needs a physical LTR fallback, clear that side inside a feature query:

css
.collection-card__status {
  left: 0.75rem;
}

@supports (inset-inline-start: 0) {
  .collection-card__status {
    left: auto;
    inset-inline-start: 0.75rem;
  }
}

This avoids keeping physical left active in a supporting RTL browser. It does not supply adaptive RTL positioning to the old browser. That requires an explicit physical RTL rule if legacy RTL support is part of the product's browser policy.

Compatibility code should correspond to a named browser requirement. Otherwise it is just a historical reenactment performed by the cascade.

Physical and logical insets compete in the cascade#

Physical and flow-relative inset properties form one logical property group. When they map to the same side, the declaration with higher cascade priority supplies the shared computed value. When direction changes, declarations that once conflicted may address different sides and both take effect.

css
.collection-card__status {
  inset-inline-start: 0.75rem;
  left: 1.5rem;
}

In LTR, both target left and the later physical declaration wins. In RTL, inset-inline-start targets right while left remains left. The box becomes constrained from both sides.

A later shorthand can quietly erase a logical inset:

css
.collection-card__status {
  inset-inline-start: 0.75rem;
  inset: auto;
}

The physical inset shorthand resets top, right, bottom and left to auto. Because it comes later, the physical side paired with inline start wins, and the logical placement no longer supplies the used offset.

When DevTools shows a logical rule but the box is not where expected, look for:

  • a later inset shorthand;
  • physical left or right from a utility class;
  • both inline sides becoming non-auto;
  • a size variant with higher specificity;
  • a later cascade layer;
  • a direction or writing-mode boundary on the positioned element;
  • an unexpected containing block.

Do not add another override until you know which declaration wins. The stylesheet is already holding a committee meeting. It does not need another attendee.

Transforms do not become logical#

Changing the inset does not change a transform. translateX() moves along the physical x-axis: positive values move right and negative values move left. It does not inspect dir and politely reverse itself.

Suppose the collection status deliberately peeks beyond the card's start edge:

Problem

css
.collection-card__status {
  --peek: -25%;

  position: absolute;
  inset-block-start: 0.75rem;
  inset-inline-start: 0;
  transform: translateX(var(--peek));
}

In LTR, the negative translation pulls the start-anchored status left, outward from the card. In RTL, the anchor moves to the right but the negative translation still moves left, back into the card.

Better

css
.collection-card__status {
  --peek: -25%;

  position: absolute;
  inset-block-start: 0.75rem;
  inset-inline-start: 0;
  transform: translateX(var(--peek));
}

:dir(rtl) .collection-card__status {
  --peek: 25%;
}

The inset follows start automatically. The directional transform changes sign explicitly. This selector expects direction to be declared with HTML dir; :dir(rtl) does not match an element merely because CSS sets direction: rtl. The guide to dir="rtl" explains that boundary in depth.

Not every transform needs flipping. A physical centering equation that offsets from the horizontal midpoint and translates backward by half the element's width works in both text directions because center is the same physical point. Replacing its left offset with inline start creates a directional problem where none existed. Keep physical geometry physical.

R033 reports a translateX that moves an element off-screen in RTL. A smaller wrong-way nudge may still need manual component testing, so do not treat the absence of that report as proof that every transform agrees with its logical anchor.

Keep left when left is the design#

left is not deprecated, hostile to Arabic or evidence of personal failure. It is the right property when the coordinate is physical.

Keep physical insets for cases such as:

  • a marker placed at an x-coordinate on a map or floor plan;
  • a label tied to the left axis of a non-mirrored chart;
  • a cue on a media timeline whose geometry remains LTR;
  • an object positioned inside a canvas-like editor;
  • physical centering based on a midpoint left offset plus a matching transform;
  • decorative artwork whose asymmetry should remain unchanged.

Use logical insets for controls, labels, badges, callouts and navigation elements whose edge follows reading direction.

The useful question is not “Does this page use Arabic?” It is “Does this edge have semantic meaning in the content flow?” If yes, use start or end. If it belongs to a fixed coordinate system, use a physical side and document that intent when the code could be mistaken for an oversight.

The element's direction controls the mapping#

Logical insets map using the positioned element's own computed writing mode and direction. An Arabic page can contain an explicitly LTR catalogue preview, and descendants inside it will treat inline start as left.

html
<main dir="rtl" lang="ar">
  <section class="catalogue-preview" dir="ltr" lang="en">
    <span class="catalogue-preview__marker">Source note</span>
  </section>
</main>
css
.catalogue-preview {
  position: relative;
}

.catalogue-preview__marker {
  position: absolute;
  inset-block-start: 0.5rem;
  inset-inline-start: 0.5rem;
}

The marker maps to physical left because it inherits LTR from the preview. The outer RTL direction does not override an explicit local boundary.

When a marker appears on the unexpected side, inspect direction and writing-mode on the marker itself. Checking only the root element can conceal a local dir, a component rule or an embedded widget. Also remember that lang="ar" identifies language but does not set direction.

Vertical writing modes change the physical mapping again. The logical property still means inline start; it may no longer mean either horizontal side. This is why aliases such as “RTL right” are convenient and slightly treacherous.

Values can be auto, positive, negative or percentages#

Inset properties accept auto, lengths and percentages. Negative values are valid and place an edge outside the reference edge:

css
.collection-card__status {
  inset-inline-start: -0.5rem;
}

In horizontal LTR, that overhangs the physical left side. In horizontal RTL, it overhangs the physical right side. Negative logical offsets are useful for intentional tabs and ribbons, but check narrow viewports and page overflow. A decorative overhang is less charming when it creates a scrollbar wider than the decoration.

Percentages resolve against the containing block's size in the relevant axis:

css
.map-annotation {
  position: absolute;
  inset-inline-start: 18%;
}

If this is a map coordinate, a physical left: 18% may be the more accurate declaration because the map does not mirror. If it is a progress marker that follows flow, the logical inset may be right. Percentage syntax does not settle the meaning.

auto leaves that side unconstrained. For a start-pinned absolute element, the common relationship is:

css
.collection-card__status {
  inset-inline-start: 0.75rem;
  inset-inline-end: auto;
}

Avoid setting both sides to non-auto by accident. Whether the box stretches or becomes overconstrained depends on its size and positioning rules, which is more behavior than a compact label requested.

Migrate positioned components in pairs#

A global replacement from left to inset-inline-start cannot tell whether a coordinate is semantic, physical or part of a centering equation. Audit one positioned component at a time.

Start by finding:

text
position:
top:
right:
bottom:
left:
inset:
transform:

Then review the declarations as a group:

  1. Identify the containing block.
  2. Describe each edge as block start, block end, inline start, inline end or physical.
  3. Preserve the current LTR geometry.
  4. Convert related padding, border and transform logic when they encode the same edge.
  5. Remove obsolete RTL overrides and physical fallbacks.
  6. Test both directions at the component's real breakpoints.

Do not stop after the inset looks correct in one screenshot. An absolute control can overlap longer Arabic copy because it is outside normal flow. A negative end offset can overhang the scrollable side of an RTL page. A fixed element can use a transformed ancestor as its containing block. These are positioning problems exposed by direction, not translations behaving badly.

Test the physical result in both directions#

Render the same component under real direction boundaries:

html
<section dir="ltr" lang="en">
  <!-- Render the collection card here. -->
</section>

<section dir="rtl" lang="ar">
  <!-- Render the same collection card here. -->
</section>

Inspect the positioned element's computed values:

js
const status = document.querySelector(".collection-card__status");
const styles = getComputedStyle(status);

console.table({
  direction: styles.direction,
  position: styles.position,
  insetInlineStart: styles.insetInlineStart,
  left: styles.left,
  right: styles.right,
});

For inset-inline-start: 12px, the physical left inset should carry that value in LTR and the physical right inset should carry it in RTL. If both physical sides are non-auto, look for a fallback, shorthand or utility that left the opposite inset active.

The element's bounding rectangle gives a second check:

js
const card = document.querySelector(".collection-card");
const status = document.querySelector(".collection-card__status");
const cardBox = card.getBoundingClientRect();
const statusBox = status.getBoundingClientRect();

console.table({
  gapFromLeft: statusBox.left - cardBox.left,
  gapFromRight: cardBox.right - statusBox.right,
});

In LTR, the start gap should appear in gapFromLeft. In RTL, it should appear in gapFromRight. Allow for borders and rounding when asserting exact values in a browser test.

Also test:

  • long Arabic titles beside the absolute element;
  • the narrowest supported viewport;
  • nested LTR content inside the RTL route;
  • size and state variants that apply inset or transforms;
  • zoom and text enlargement;
  • sticky behavior across the full scroll range;
  • fixed elements inside transformed ancestors;
  • negative offsets and document width;
  • focus outlines and pointer targets near clipped edges.

R030 reports direction-sensitive physical properties without an RTL override. That helps locate left and right declarations that need classification, not automatic replacement. If a positioned element causes document-level horizontal overflow, R040 reports the overflow; use the RTL overflow guide to trace the responsible bounding box and declaration.

Common mistakes#

Replacing right with inset-inline-start#

If right represents the LTR trailing edge, replace it with inset-inline-end. A valid logical conversion must leave the LTR layout unchanged.

Forgetting position#

Logical insets have no effect on position: static. Inspect the computed position before adding stronger selectors or larger offset values.

Setting both inline insets unintentionally#

inset-inline: 1rem sets start and end. An auto-sized absolute box can stretch between them. Use a longhand when only one edge should constrain the box.

Mirroring the anchor but not the transform#

The inset follows writing direction. translateX() remains physical. Flip a genuinely directional translation or keep a physical positioning equation when the design is physical.

Blaming the property for the wrong containing block#

Logical and physical insets both use the active containing block. Find the ancestor establishing it before changing the side.

Make the edge say what it means#

Use inset-inline-start when a positioned element belongs at reading start. Use inset-inline-end when it belongs at reading end. Keep left when left is a coordinate, not an English-language habit.

A free Ritla scan can surface direction-sensitive physical positioning through R030, after which each result still needs that semantic decision. Add the corrected component states to the Arabic QA checklist for frontend engineers, and test transforms and containing blocks along with the inset. A badge attached to the correct edge of the wrong box remains impressively misplaced.

FAQ

Is inset-inline-start always the same as right in RTL?

It maps to right in horizontal RTL writing. In vertical writing modes, inline start may map to top or bottom. The element's own computed writing mode and direction determine the side.

Does inset-inline-start work without position?

It has no effect on a statically positioned element. Use it with position: relative, absolute, fixed or sticky, and remember that each mode interprets the inset differently.

Is inset-inline: 0 the same as inset-inline-start: 0?

No. The shorthand sets both inline start and inline end to zero. On an absolutely positioned auto-sized box, that can stretch the box across the containing block.

Should physical centering use logical inset?

Not automatically. A midpoint left offset paired with a negative half-width translation describes a physical center and works in both directions. Converting only the inset to logical start breaks the equation in RTL unless the transform also changes. The simpler answer is to keep a correct physical centering rule or use layout alignment.

Why are both left and right non-auto in RTL?

A physical fallback, later shorthand or utility may be setting the opposite side after logical mapping. Inspect the cascade and remove or reset the physical declaration rather than compensating with another offset.

Checks in this guide

See what your Arabic pages are hiding

Paste a URL. Ritla renders the page on desktop and mobile, runs every check, and shows the top issues with screenshot evidence.