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.

  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.