@clerk/themes
Version:
Themes for the Clerk auth components
233 lines (232 loc) • 9.54 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.clerkCssVar = void 0;
exports.extractCSSVariableValue = extractCSSVariableValue;
exports.extractCSSVariableValueWithFallback = extractCSSVariableValueWithFallback;
exports.extractMultipleCSSVariables = extractMultipleCSSVariables;
exports.isCSSVariable = isCSSVariable;
exports.resolveCSSVariable = resolveCSSVariable;
exports.resolveComputedCSSProperty = resolveComputedCSSProperty;
exports.resolveComputedCSSColor = resolveComputedCSSColor;
/**
* Extracts the computed value of a CSS custom property (CSS variable)
* @param variableName - The CSS variable name in any of these formats:
* - 'var(--color)'
* - '--color'
* - 'color' (will be prefixed with --)
* @param element - Optional element to get the variable from (defaults to document.documentElement)
* @returns The computed CSS variable value as a string, or null if not found
* @example
* const colorValue = extractCSSVariableValue('var(--color)'); // "red"
* const colorValue2 = extractCSSVariableValue('--color'); // "red"
* const colorValue3 = extractCSSVariableValue('color'); // "red"
* const colorValue4 = extractCSSVariableValue('--nonexistent'); // null
* const colorValue5 = extractCSSVariableValue('--nonexistent', document.body); // null
* const colorValue6 = extractCSSVariableValue('--nonexistent', document.body, '#000000'); // "#000000"
*/
function extractCSSVariableValue(variableName, element) {
// Handle both browser and server environments
if (typeof window === 'undefined' || typeof getComputedStyle === 'undefined') {
return null;
}
// Handle different input formats
let cleanVariableName;
if (variableName.startsWith('var(') && variableName.endsWith(')')) {
// Extract from 'var(--color)' format
cleanVariableName = variableName.slice(4, -1).trim();
}
else if (variableName.startsWith('--')) {
// Already in '--color' format
cleanVariableName = variableName;
}
else {
// Add -- prefix to 'color' format
cleanVariableName = `--${variableName}`;
}
// Use provided element or default to document root
// Handle cases where document might not be available or element might be null
let targetElement;
try {
if (element) {
targetElement = element;
}
else if (typeof document !== 'undefined' && document.documentElement) {
targetElement = document.documentElement;
}
else {
return null;
}
}
catch {
return null;
}
// Get computed style and extract the variable value
try {
const computedStyle = getComputedStyle(targetElement);
const value = computedStyle.getPropertyValue(cleanVariableName).trim();
return value || null;
}
catch {
return null;
}
}
/**
* Alternative version that also accepts fallback values
* @param variableName - The CSS variable name
* @param fallback - Fallback value if variable is not found
* @param element - Optional element to get the variable from
* @returns The CSS variable value or fallback
*/
function extractCSSVariableValueWithFallback(variableName, fallback, element) {
const value = extractCSSVariableValue(variableName, element);
return value || fallback;
}
/**
* Gets multiple CSS variables at once
* @param variableNames - Array of CSS variable names
* @param element - Optional element to get variables from
* @returns Object mapping variable names to their values
* @example
* const variables = extractMultipleCSSVariables([
* '--primary-color',
* '--secondary-color',
* '--font-size'
* ]);
*/
function extractMultipleCSSVariables(variableNames, element) {
return variableNames.reduce((acc, varName) => {
acc[varName] = extractCSSVariableValue(varName, element);
return acc;
}, {});
}
/**
* Checks if a given value represents a CSS variable (var() function)
* @param value - The value to check
* @returns True if the value is a CSS variable, false otherwise
* @example
* isCSSVariable('var(--color)'); // true
* isCSSVariable('var(--color, red)'); // true
* isCSSVariable('--color'); // false
* isCSSVariable('red'); // false
* isCSSVariable('#ff0000'); // false
*/
function isCSSVariable(value) {
if (!value || typeof value !== 'string') {
return false;
}
const trimmed = value.trim();
// Must start with var( and end with )
if (!trimmed.startsWith('var(') || !trimmed.endsWith(')')) {
return false;
}
// Extract content between var( and )
const content = trimmed.slice(4, -1).trim();
// Must start with --
if (!content.startsWith('--')) {
return false;
}
// Find the variable name (everything before the first comma, if any)
const commaIndex = content.indexOf(',');
const variableName = commaIndex === -1 ? content : content.slice(0, commaIndex).trim();
// Variable name must be valid (--something)
return /^--[a-zA-Z0-9-_]+$/.test(variableName);
}
/**
* Resolves a CSS variable to its computed value, with fallback support
* Handles var() syntax and extracts variable name and fallback value
* @param value - The CSS variable string (e.g., 'var(--color, red)')
* @param element - Optional element to get the variable from
* @returns The resolved value or null if not found and no fallback provided
* @example
* resolveCSSVariable('var(--primary-color)'); // "blue" (if --primary-color is blue)
* resolveCSSVariable('var(--missing-color, red)'); // "red" (fallback)
* resolveCSSVariable('var(--missing-color)'); // null
* resolveCSSVariable('red'); // null (not a CSS variable)
*/
function resolveCSSVariable(value, element) {
if (!isCSSVariable(value)) {
return null;
}
// Extract content between var( and )
const content = value.trim().slice(4, -1).trim();
// Find the variable name and fallback value
const commaIndex = content.indexOf(',');
let variableName;
let fallbackValue = null;
if (commaIndex === -1) {
variableName = content;
}
else {
variableName = content.slice(0, commaIndex).trim();
fallbackValue = content.slice(commaIndex + 1).trim();
}
// Try to get the resolved variable value
const resolvedValue = extractCSSVariableValue(variableName, element);
if (resolvedValue) {
return resolvedValue;
}
// If variable couldn't be resolved, check if fallback is also a CSS variable
if (fallbackValue) {
if (isCSSVariable(fallbackValue)) {
// Recursively resolve nested CSS variables
const recursiveResult = resolveCSSVariable(fallbackValue, element);
return recursiveResult;
}
return fallbackValue;
}
return null;
}
/**
* Resolves a CSS property to its computed value, in the context of a DOM element
* This is used to resolve CSS variables to their computed values, in the context of a DOM element.
*
* @param parentElement - The parent element to resolve the property in the context of
* @param propertyName - The CSS property name (e.g., 'color', 'font-weight', 'font-size')
* @param propertyValue - The property value to resolve (can be a CSS variable)
* @returns The resolved property value as a string
*/
function resolveComputedCSSProperty(parentElement, propertyName, propertyValue) {
const element = document.createElement('div');
element.style.setProperty(propertyName, propertyValue);
parentElement.appendChild(element);
const computedStyle = window.getComputedStyle(element);
const computedValue = computedStyle.getPropertyValue(propertyName);
parentElement.removeChild(element);
return computedValue.trim();
}
/**
* Resolves a color to its computed value, in the context of a DOM element
* This is used to resolve CSS variables to their computed values, in the context of a DOM element to support passing
* CSS variables to Stripe Elements.
*
* @param parentElement - The parent element to resolve the color in the context of
* @param color - The color to resolve
* @param backgroundColor - The background color to use for the canvas, this is used to ensure colors that
* contain an alpha value mix together correctly. So the output matches the alpha usage in the CSS.
* @returns The resolved color as a hex string
*/
function resolveComputedCSSColor(parentElement, color, backgroundColor = 'white') {
const computedColor = resolveComputedCSSProperty(parentElement, 'color', color);
const computedBackgroundColor = resolveComputedCSSProperty(parentElement, 'color', backgroundColor);
const canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
const ctx = canvas.getContext('2d');
if (!ctx) {
return computedColor;
}
ctx.fillStyle = computedBackgroundColor;
ctx.fillRect(0, 0, 1, 1);
ctx.fillStyle = computedColor;
ctx.fillRect(0, 0, 1, 1);
const { data } = ctx.getImageData(0, 0, 1, 1);
return `#${data[0].toString(16).padStart(2, '0')}${data[1].toString(16).padStart(2, '0')}${data[2].toString(16).padStart(2, '0')}`;
}
/**
* Creates a CSS variable prefixed with `clerk-` with a default value
* @param name - The name of the CSS variable
* @param defaultValue - The default value
* @returns The CSS variable string
*/
const clerkCssVar = (name, defaultValue) => `var(--clerk-${name}, ${defaultValue})`;
exports.clerkCssVar = clerkCssVar;