UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

82 lines (81 loc) • 2.93 kB
import { cssFitValues } from "../internal.js"; // Standard CSS object-position keyword values. // These cover the vast majority of real-world usage and are emitted as // data-attribute-driven CSS rules so that no inline styles are needed (CSP-safe). const POSITION_KEYWORDS = [ "top", "bottom", "left", "right", "center" ]; // CSS <position> two-value syntax requires one keyword per axis. // Same-axis pairs like `top bottom` or `left right` are invalid. const VERTICAL_ONLY = new Set(["top", "bottom"]); const HORIZONTAL_ONLY = new Set(["left", "right"]); /** * Builds every valid 1- and 2-keyword combination of object-position values: * center, top, bottom, left, right, top left, top center, top right, etc. * Same-axis pairs (e.g. `top bottom`, `left right`) are excluded because * the CSS `<position>` grammar forbids two keywords on the same axis. * Returns entries as `[dataAttrValue, cssValue]` pairs where the data-attr * value has spaces replaced with dashes (matching the normalisation in internal.ts). */ function getPositionEntries() { const entries = []; // Single-keyword values for (const kw of POSITION_KEYWORDS) { entries.push([kw, kw]); } // Two-keyword combinations (cross-axis only) for (const a of POSITION_KEYWORDS) { for (const b of POSITION_KEYWORDS) { if (a === b) continue; if (VERTICAL_ONLY.has(a) && VERTICAL_ONLY.has(b)) continue; if (HORIZONTAL_ONLY.has(a) && HORIZONTAL_ONLY.has(b)) continue; const cssValue = `${a} ${b}`; const dataAttr = `${a}-${b}`; entries.push([dataAttr, cssValue]); } } return entries; } export function generateImageStylesCSS(defaultObjectFit, defaultObjectPosition) { const fitStyles = cssFitValues.map((fit) => ` [data-astro-image-fit="${fit}"] { object-fit: ${fit}; }`).join("\n"); const defaultFitStyle = defaultObjectFit && cssFitValues.includes(defaultObjectFit) ? ` :where([data-astro-image]:not([data-astro-image-fit])) { object-fit: ${defaultObjectFit}; }` : ""; // Generate rules for all standard object-position keyword combinations. // This avoids inline styles entirely, keeping everything CSP-compliant. const positionEntries = getPositionEntries(); const positionStyles = positionEntries.map(([dataAttr, cssValue]) => ` [data-astro-image-pos="${dataAttr}"] { object-position: ${cssValue}; }`).join("\n"); const defaultPositionStyle = defaultObjectPosition ? ` :where([data-astro-image]:not([data-astro-image-pos])) { object-position: ${defaultObjectPosition}; }` : ""; const rules = ` :where([data-astro-image]) { height: auto; } :where([data-astro-image="full-width"]) { width: 100%; } :where([data-astro-image="constrained"]) { max-width: 100%; } ${fitStyles} ${defaultFitStyle} ${positionStyles} ${defaultPositionStyle} `.trim(); // Wrap in a @layer so these low-priority defaults don't override user // styles that live inside CSS cascade layers. return `@layer astro.images {\n${rules}\n}`; }