Skip to content

How It Works

PostCSS Logical Polyfill processes CSS through a sophisticated 7-phase optimization pipeline that intelligently transforms logical properties into physical properties while preserving directional behavior.

When generating optimized direction-specific rules, repeated physical properties are resolved by declaration priority: !important takes precedence, and the last declaration wins among declarations of equal importance.

When a common shorthand and a direction-dependent longhand overlap (for example, margin and margin-left), the direction rule also includes the overlapping declarations in their original winning order. This prevents selector specificity from changing the cascade. Each property still appears only once within an optimized rule.

CSS nesting must be expanded before this plugin (for example, with postcss-nested or postcss-nesting). Unexpanded nested rules are left intact with a PostCSS warning. Logical properties use the horizontal writing mode; vertical writing modes are not inferred.

Direction prefixes add selector specificity. Overrides in separate rules should use matching direction scopes. For subtrees with conflicting direction ancestors, write explicit direction-scoped selectors so the plugin can use the rightmost context.

  1. Detection Phase

    The plugin scans the CSS AST to identify:

    • Logical properties (margin-inline, padding-block, etc.)
    • Existing direction selectors ([dir=“ltr”], [dir=“rtl”])
    • Scoped vs unscoped logical properties
  2. Classification Phase

    Properties are categorized into three types:

    • Block-direction: Only affect vertical dimension
    • Inline-direction: Affect horizontal dimension (direction-aware)
    • Mixed-direction: Affect both dimensions
  3. Transformation Phase

    Each logical property is converted based on:

    • Property type (block/inline/mixed)
    • Current direction context (LTR/RTL)
    • Whether the rule is already scoped
  4. Selector Application

    Direction selectors are added when needed:

    • Block properties: No selectors needed
    • Inline properties: Separate LTR/RTL rules
    • Mixed properties: Direction-specific rules
  5. Optimization Phase

    The CSS is optimized by:

    • Merging compatible rules
    • Eliminating redundant declarations
    • Consolidating selectors
  6. Priority Management

    Implements rightmost selector precedence:

    • Later rules override earlier ones
    • Direction-specific rules take precedence
    • Maintains CSS cascade order
  7. Output Generation

    Produces clean, optimized CSS:

    • Minimal duplication
    • Consistent selector patterns
    • Preserved source order

Block-direction properties only affect the block dimension (usually vertical) and are direction-independent.

Examples

  • margin-block
  • padding-block-start
  • border-block-end
  • overflow-block

Transformation

Simple 1:1 mapping to physical properties without direction selectors

/* Input */
.element {
margin-block: 1rem;
padding-block-start: 0.5rem;
}
/* Output - No direction selectors needed */
.element {
margin-top: 1rem;
margin-bottom: 1rem;
padding-top: 0.5rem;
}

Inline-direction properties affect the inline dimension (usually horizontal) and are direction-aware.

Examples

  • margin-inline
  • padding-inline-start
  • border-inline-end
  • overflow-inline

Transformation

Generate separate LTR and RTL rules with direction selectors

/* Input */
.element {
margin-inline: 1rem;
padding-inline-start: 0.5rem;
}
/* Output - Separate direction rules */
.element {
margin-left: 1rem;
margin-right: 1rem;
}
[dir="ltr"] .element {
padding-left: 0.5rem;
}
[dir="rtl"] .element {
padding-right: 0.5rem;
}

Mixed-direction properties affect both dimensions and require careful handling.

Examples

  • inset
  • border-radius logical variants

Transformation

Generate direction-specific rules for inline components

/* Input */
.element {
inset: 10px 20px;
}
/* Output - Direction-aware for horizontal values */
[dir="ltr"] .element {
top: 10px;
right: 20px;
bottom: 10px;
left: 20px;
}
[dir="rtl"] .element {
top: 10px;
left: 20px;
bottom: 10px;
right: 20px;
}

Properties without existing direction selectors are processed to generate both LTR and RTL versions.

/* Input - Unscoped */
.container {
margin-inline-start: 1rem;
}
/* Output - Both directions generated */
[dir="ltr"] .container {
margin-left: 1rem;
}
[dir="rtl"] .container {
margin-right: 1rem;
}

Properties already within direction selectors are processed for that specific direction only.

/* Input - Already scoped */
[dir="ltr"] .container {
margin-inline-start: 1rem;
}
/* Output - Only LTR processed */
[dir="ltr"] .container {
margin-left: 1rem;
}

The plugin includes an integrated shim system that extends support beyond the core postcss-logical package.

Scroll Properties

  • scroll-margin-*
  • scroll-padding-*

Overflow Properties

  • overflow-block
  • overflow-inline

Containment Properties

  • contain-intrinsic-block-size
  • contain-intrinsic-inline-size

Logical Values

  • float: inline-start
  • clear: inline-end
  • resize: block
// Shim declaration example
'overflow-block': (decl) => {
decl.cloneBefore({ prop: 'overflow-y' });
decl.remove();
}

Compatible rules are merged to reduce CSS size:

/* Before merging */
.element { margin-left: 1rem; }
.element { margin-right: 1rem; }
/* After merging */
.element {
margin-left: 1rem;
margin-right: 1rem;
}

Similar selectors are consolidated when possible:

/* Multiple rules with same selector */
[dir="ltr"] .a { margin-left: 1rem; }
[dir="ltr"] .a { padding-left: 0.5rem; }
/* Consolidated */
[dir="ltr"] .a {
margin-left: 1rem;
padding-left: 0.5rem;
}

Duplicate or unnecessary declarations are removed:

/* Input with redundancy */
.element {
margin-inline: 1rem;
margin-inline: 1rem; /* Duplicate */
}
/* Output - Duplicates removed */
.element {
margin-left: 1rem;
margin-right: 1rem;
}

The plugin minimizes AST traversals by:

  • Single-pass processing for most transformations
  • Batching similar operations
  • Caching selector analyses

Optimizes memory by:

  • Processing declarations in-place when possible
  • Removing transformed declarations immediately
  • Reusing selector objects

Reduces output size through:

  • Block-direction optimization (no direction selectors)
  • Rule merging and consolidation
  • Elimination of redundant declarations

The plugin gracefully handles invalid logical properties:

/* Invalid property value */
.element {
margin-inline: invalid-value;
}
/* Preserved as-is with warning */
.element {
margin-inline: invalid-value;
}

Malformed CSS is preserved to avoid breaking builds:

/* Malformed rule */
.element {
margin-inline: 1rem
/* Missing semicolon */
}
/* Preserved with transformation */
.element {
margin-left: 1rem;
margin-right: 1rem
}

Enable detailed logging during development:

const result = postcss([
logicalPolyfill({ debug: true })
]).process(css);
// Logs transformation details
console.log(result.messages);

The plugin preserves source map information for debugging:

const result = postcss([logicalPolyfill()])
.process(css, {
from: 'input.css',
to: 'output.css',
map: true
});

Place postcss-logical-polyfill after CSS processing plugins but before optimization plugins:

module.exports = {
plugins: [
require('postcss-import'),
require('postcss-nested'),
require('postcss-logical-polyfill'), // After CSS processing
require('autoprefixer'),
require('cssnano') // Before optimization
]
};

The plugin is compatible with:

  • Autoprefixer: Works together seamlessly
  • CSSnano: Optimization works on transformed output
  • PostCSS Nested: Processes nested logical properties
  • Tailwind CSS: Transforms Tailwind’s logical utilities

Processor Core

Main transformation engine that coordinates all phases

Shim System

Extension system for additional logical properties

Selector Utils

Utilities for parsing and manipulating direction selectors

Property Maps

Mapping tables for logical to physical property conversion

CSS Input
↓
AST Parsing
↓
Property Detection
↓
Classification
↓
Transformation
↓
Optimization
↓
CSS Output

This architecture ensures reliable, performant transformation of logical properties while maintaining CSS semantics and optimizing output quality.