PostCSS plugin that transforms CSS :focus-within pseudo-selectors with browser polyfill support
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
The browser polyfill provides :focus-within behavior in browsers that don't support the native pseudo-class by dynamically applying attributes or classes to elements when their descendants receive focus.
Initializes the focus-within polyfill in the browser environment. The polyfill detects browser support and only activates if needed (unless forced).
/**
* Initialize the focus-within polyfill in the browser
* @param options - Polyfill configuration options
*/
function focusWithin(options?: BrowserOptions): void;
interface BrowserOptions {
/** Force polyfill to run even if browser supports :focus-within. Default: false */
force?: boolean;
/** The replacement selector (class or attribute). Default: "[focus-within]" */
replaceWith?: string;
}Usage Examples:
import focusWithin from "postcss-focus-within/browser";
// Basic initialization with default options
focusWithin();
// Force polyfill to run regardless of browser support
focusWithin({ force: true });
// Use custom replacement selector
focusWithin({ replaceWith: ".focus-within" });
// Combined options
focusWithin({
force: false,
replaceWith: "[data-focus-within]"
});Global script usage:
Note: When using the global script, the function is available as focusWithinInit (not focusWithin).
<script src="https://unpkg.com/postcss-focus-within@9.0.1/dist/browser-global.js"></script>
<script>
focusWithinInit({ replaceWith: "[focus-within]" });
</script>Controls whether the polyfill runs even when the browser natively supports :focus-within.
force?: boolean; // Default: falseUsage:
// Only run if browser lacks support (default)
focusWithin({ force: false });
// Always run polyfill
focusWithin({ force: true });Specifies the attribute or class to apply to elements when they contain focused descendants. Must match the replaceWith option used in the PostCSS plugin.
replaceWith?: string; // Default: "[focus-within]"Supported formats:
// Attribute selector (default)
focusWithin({ replaceWith: "[focus-within]" });
// Class selector
focusWithin({ replaceWith: ".focus-within" });
// Custom attribute
focusWithin({ replaceWith: "[data-focus]" });The polyfill automatically detects browser support by attempting to use document.querySelector(':focus-within'). If this throws an error, the polyfill activates.
try {
document.querySelector(':focus-within');
// Browser supports :focus-within, polyfill won't run (unless forced)
} catch (error) {
// Browser doesn't support :focus-within, polyfill will run
}The polyfill handles various document ready states:
if (document.readyState === 'complete') {
// Initialize immediately
} else {
// Wait for DOMContentLoaded event
document.addEventListener('DOMContentLoaded', initialize);
}The polyfill manages focus state by listening to focus and blur events:
// Event listeners are attached with capture: true
document.addEventListener('focus', handleFocusChange, true);
document.addEventListener('blur', handleFocusChange, true);When focus changes, the polyfill:
Example behavior:
<!-- Before focus -->
<form class="my-form">
<fieldset class="fieldset">
<input type="text" id="name">
</fieldset>
</form>
<!-- After focusing input with replaceWith: "[focus-within]" -->
<form class="my-form" focus-within>
<fieldset class="fieldset" focus-within>
<input type="text" id="name">
</fieldset>
</form>The polyfill automatically adds the js-focus-within class to the document element to indicate it's active:
document.documentElement.className += ' js-focus-within';This class is used by the PostCSS plugin to scope fallback selectors and prevent flash of unstyled content.
The polyfill validates the replaceWith option and throws an error for invalid selectors:
// These will throw errors:
focusWithin({ replaceWith: ".class > .child" }); // Contains >
focusWithin({ replaceWith: "[attr]:hover" }); // Contains :
focusWithin({ replaceWith: "#id" }); // Contains #Error example:
try {
focusWithin({ replaceWith: "#invalid" });
} catch (error) {
console.error(error.message);
// "#invalid is not a valid replacement since it can't be applied to single elements."
}For Next.js applications, use dynamic imports to ensure the polyfill only runs in the browser:
import { useEffect } from 'react';
useEffect(async () => {
const focusWithin = (await import('postcss-focus-within/browser')).default;
focusWithin();
}, []);Install with Tessl CLI
npx tessl i tessl/npm-postcss-focus-within