// ── Theme → CloverElementStyles compiler ──────────────────────── // // Translates the high-level `CloverFieldTheme` config into the JSON shape // Clover's `elements.create()` expects. // // Clover honors per-element selectors (`card-number input`) reliably; global // selectors like `'::placeholder'` are inconsistently applied across Clover // versions. We emit per-element rules for every field so styling is // deterministic. The `'input'` global rule is still emitted as a baseline. // // Key invariants the legacy plugin established (mirrored here so the visual // output matches what merchants are already used to): // - Input fills its iframe: height: 100%, width: 100%, no padding/margin. // - Card number + cardholder name + email get a left text-indent so the // placeholder doesn't sit flush against the iframe edge. // - Date / CVV / postal-code text is centered. import type { CloverCssProperties, CloverElementStyles, CloverFieldTheme } from './types'; const PLACEHOLDER_PSEUDOS = [ '::placeholder', '::-webkit-input-placeholder', '::-moz-placeholder', ':-ms-input-placeholder', ':-moz-placeholder', ] as const; // Clover's element-scoped CSS selector is the lower-kebab form of the element // type name. `CARD_EMAIL_ADDRESS` is `card-email-address`, NOT `card-email` — // the short form is silently ignored (verified via discovery 2026-05-18). The // legacy WP plugin had the same bug, which is why the email placeholder sat // flush against the iframe edge while the name field was indented correctly. const LEFT_ALIGNED_FIELDS = ['card-number', 'card-name', 'card-email-address'] as const; const CENTER_ALIGNED_FIELDS = ['card-date', 'card-cvv', 'card-postal-code'] as const; export function compileTheme(theme?: CloverFieldTheme): CloverElementStyles { const styles: CloverElementStyles = {}; // ── Global input baseline. Applies to every Clover element's input. ── // // `height` must be a definite length, not `100%`. The Clover iframe document's // ``/`
` don't have an explicit height, so `100%` resolves to the // input's content-box height (~17px) and clicks on the rest of the 46px // wrapper land on the iframe body instead of the input. Matching the wrapper // height in `styles.ts` (46px) keeps the entire wrapper clickable; the // `overflow: hidden` on `.wcp-pf__field` absorbs any sub-pixel overshoot. // `lineHeight` matches so text is vertically centered without depending on // flex/grid we don't control inside the iframe. `border: 'none'` suppresses // Clover's default input border — the SDK's wrapper owns the border. const inputHeight = theme?.inputHeight ?? '46px'; styles.input = stripUndefined