PostCSS plugin to rebase, inline, or copy assets from url() declarations with support for multiple transformation modes and advanced filtering options.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
PostCSS URL provides advanced encoding capabilities with automatic MIME type detection, SVG optimization, and multiple encoding strategies for optimal file size and browser compatibility.
The plugin automatically detects file types using the mime package and selects appropriate encoding strategies.
/**
* File interface with MIME type detection
*/
interface File {
/** Absolute path to the asset file */
path: string;
/** File contents as Buffer */
contents: Buffer;
/** MIME type detected from file extension */
mimeType: string;
}
interface MimeTypeHandling {
/** Automatically detected MIME types */
detection: 'mime.getType(filePath)';
/** Default encoding per type */
defaults: {
'image/svg+xml': 'encodeURIComponent';
'image/png': 'base64';
'image/jpeg': 'base64';
'image/gif': 'base64';
'image/webp': 'base64';
[key: string]: 'base64' | 'encodeURI' | 'encodeURIComponent';
};
}Usage Example:
const postcss = require('postcss');
const postcssUrl = require('postcss-url');
// MIME type automatically detected from file extension
const result = await postcss()
.use(postcssUrl({
url: 'inline'
// SVG files: encodeURIComponent (default)
// PNG/JPG/GIF: base64 (default)
// Override with encodeType option if needed
}))
.process(css, { from: 'src/main.css', to: 'dist/main.css' });Three encoding strategies available for converting files to data URIs with different characteristics.
/**
* Available encoding types for inline mode
*/
type EncodeType = 'base64' | 'encodeURI' | 'encodeURIComponent';
interface EncodingCharacteristics {
base64: {
/** Universal browser support */
compatibility: 'all browsers';
/** ~33% larger than original file */
sizeIncrease: '33%';
/** Best for binary files (images, fonts) */
useCases: 'binary files';
};
encodeURI: {
/** Good browser support */
compatibility: 'modern browsers';
/** Smaller than base64 for text files */
sizeIncrease: 'varies';
/** Best for text-based files */
useCases: 'text files like SVG';
};
encodeURIComponent: {
/** Good browser support */
compatibility: 'modern browsers';
/** More encoding than encodeURI */
sizeIncrease: 'varies';
/** Safest for complex content */
useCases: 'complex SVG files';
};
}Encoding Comparison Example:
// Base64 encoding (default for images)
const base64Result = await postcss()
.use(postcssUrl({
url: 'inline',
encodeType: 'base64'
}))
.process(css, { from: 'src/main.css', to: 'dist/main.css' });
// Result: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...
// URI Component encoding (default for SVG)
const uriResult = await postcss()
.use(postcssUrl({
url: 'inline',
encodeType: 'encodeURIComponent'
}))
.process(css, { from: 'src/main.css', to: 'dist/main.css' });
// Result: data:image/svg+xml,%3Csvg xmlns%3D%22http%3A//www.w3.org...
// URI encoding (less aggressive than encodeURIComponent)
const simpleUriResult = await postcss()
.use(postcssUrl({
url: 'inline',
encodeType: 'encodeURI'
}))
.process(css, { from: 'src/main.css', to: 'dist/main.css' });
// Result: data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org...Advanced SVG encoding optimization reduces file size while maintaining compatibility with IE9+ and Android 3+.
/**
* SVG optimization configuration
*/
interface SvgOptimization {
/** Enable optimized SVG encoding */
optimizeSvgEncode: boolean;
/** Optimization techniques applied */
techniques: {
/** Replace common encoded characters with literals */
literalReplacement: '%3D → =, %3A → :, %2F → /, etc.';
/** Convert hex escapes to lowercase for better gzip */
lowercaseHex: '%2C → %2c';
/** Remove newlines from SVG content */
newlineRemoval: 'automatic';
/** Replace %20 with spaces */
spaceOptimization: '%20 → space';
/** Properly encode hash characters */
hashEncoding: '# → %23';
};
}SVG Optimization Example:
// Optimized SVG inlining
const optimizedResult = await postcss()
.use(postcssUrl({
url: 'inline',
encodeType: 'encodeURIComponent',
optimizeSvgEncode: true // Enable SVG optimizations
}))
.process(css, { from: 'src/main.css', to: 'dist/main.css' });
// Without optimization:
// data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E
// With optimization:
// data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg"%3EOptimization Algorithm Details:
// Internal optimization process (from encode.js)
const optimizationSteps = {
1: 'encodeURIComponent(svgContent)',
2: 'replace common encoded chars: %3D→=, %3A→:, %2F→/, %22→\', %2C→,, %3B→;',
3: 'lowercase hex escapes: %2C → %2c for better gzip compression',
4: 'remove newlines from content',
5: 'replace %20 with spaces',
6: 'encode # as %23 for URL safety'
};Special handling for SVG files with fragment identifiers (hash links) in URLs.
/**
* URI fragment handling options
*/
interface FragmentHandling {
/** Include fragment in inlined data URI */
includeUriFragment: boolean;
/** Suppress warnings about SVG fragments */
ignoreFragmentWarning: boolean;
/** Fragment behavior */
behavior: {
/** With includeUriFragment: true */
included: 'data:image/svg+xml,...#fragment';
/** With includeUriFragment: false (default) */
excluded: 'data:image/svg+xml,...';
/** Warning message for SVG fragments */
warning: 'Image type is svg and link contains #. Postcss-url cant handle svg fragments. SVG file fully inlined.';
};
}Fragment Handling Examples:
// Default behavior - fragments ignored with warning
const defaultResult = await postcss()
.use(postcssUrl({
url: 'inline'
// includeUriFragment: false (default)
// ignoreFragmentWarning: false (default)
}))
.process(`background: url('icon.svg#circle');`, {
from: 'src/main.css',
to: 'dist/main.css'
});
// Warning: "Image type is svg and link contains #..."
// Result: background: url('data:image/svg+xml,...'); // Fragment removed
// Include fragments in data URI
const fragmentResult = await postcss()
.use(postcssUrl({
url: 'inline',
includeUriFragment: true
}))
.process(`background: url('icon.svg#circle');`, {
from: 'src/main.css',
to: 'dist/main.css'
});
// Result: background: url('data:image/svg+xml,...#circle'); // Fragment preserved
// Suppress fragment warnings
const quietResult = await postcss()
.use(postcssUrl({
url: 'inline',
ignoreFragmentWarning: true
}))
.process(`background: url('icon.svg#circle');`, {
from: 'src/main.css',
to: 'dist/main.css'
});
// No warning issued, fragment still removedAutomatic quote management for percent-encoded SVG URIs to ensure proper CSS syntax.
/**
* Automatic quote handling for encoded URIs
*/
interface QuoteHandling {
/** Automatic quote detection and wrapping */
behavior: {
/** SVG with encodeURIComponent gets wrapped in quotes */
svgEncoded: '"data:image/svg+xml,%3Csvg..."';
/** Base64 encoded files don't get quotes */
base64: 'data:image/png;base64,iVBORw0...';
/** Quote preservation from original URL */
preservation: 'maintains existing quote style when possible';
};
}Quote Handling Example:
// Automatic quote wrapping for encoded SVG
const result = await postcss()
.use(postcssUrl({
url: 'inline',
encodeType: 'encodeURIComponent'
}))
.process(`background: url(icon.svg);`, { // No quotes in source
from: 'src/main.css',
to: 'dist/main.css'
});
// Result: background: url("data:image/svg+xml,%3Csvg..."); // Quotes added
// Base64 doesn't get automatic quotes
const base64Result = await postcss()
.use(postcssUrl({
url: 'inline',
encodeType: 'base64'
}))
.process(`background: url(image.png);`, { // No quotes in source
from: 'src/main.css',
to: 'dist/main.css'
});
// Result: background: url(data:image/png;base64,iVBOR...); // No quotes addedEncoding process includes comprehensive error handling for missing files, invalid MIME types, and encoding failures.
/**
* Encoding error handling
*/
interface EncodingErrors {
/** Missing file handling */
missingFile: 'warn and skip processing';
/** Invalid MIME type handling */
invalidMimeType: 'warn and skip processing';
/** Encoding failure handling */
encodingFailure: 'fallback to specified fallback mode';
/** Size limit exceeded */
sizeExceeded: 'fallback to specified fallback mode';
}Error Handling Examples:
// Missing file with fallback
const result = await postcss()
.use(postcssUrl({
url: 'inline',
fallback: 'rebase' // Fallback when file not found
}))
.process(`background: url('missing.png');`, {
from: 'src/main.css',
to: 'dist/main.css'
});
// Warning: "Can't read file 'missing.png', ignoring"
// Falls back to rebase mode
// Size limit with fallback
const sizeResult = await postcss()
.use(postcssUrl({
url: 'inline',
maxSize: 1, // 1KB limit
fallback: 'copy'
}))
.process(`background: url('large-image.png');`, {
from: 'src/main.css',
to: 'dist/main.css'
});
// Large file automatically uses copy mode instead of inlineInstall with Tessl CLI
npx tessl i tessl/npm-postcss-url