> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dakota.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Cards.js Theming & Internationalization

> The DOM recipe, the style contract, custom markup rules, custom properties, the theme option, and i18n

<Warning>
  **Cards is available in sandbox only while we finish development.** Dakota enables Cards per
  account. The Cards endpoints are in the [API reference](/api-reference/introduction), marked
  **Sandbox only**. Endpoints, fields, and flows can still change before release.
</Warning>

Card details are styled in three independent layers. Reach for the lowest one that gets you the look you need:

1. **[The default stylesheet](#the-default-stylesheet)** — a finished card face you import and use as-is.
2. **[Your own CSS](#restyle-with-css)** — override custom properties, or re-declare any rule, on the documented DOM recipe.
3. **[The `theme` option](#the-theme-option)** — text styling that carries *into* the revealed value frames.

## The default stylesheet

```ts theme={null}
import '@dakota-xyz/cards-js/styles.css';
```

This paints the Dakota card face: a black card with a diagonal charcoal gradient, the DAKOTA wordmark top-left, the card-network mark top-right, and a grain-textured disc in the bottom-right — with a Barlow PAN and Lato labels. The brand marks and the disc are inlined as SVG, and the fonts are self-hosted inside the package and served from your own origin, so rendering the face makes no third-party requests.

## The DOM recipe

Both the vanilla and React integrations render the same structure. The React `<CardDetails>` component produces it for you. In plain JS, you write it yourself and point the mount targets at the four value nodes:

```html theme={null}
<div class="dk-card">
  <div class="dk-card__pan" id="pan"></div>
  <div class="dk-card__row">
    <div class="dk-card__group">
      <span class="dk-card__label">VALID THRU</span>
      <span class="dk-card__expiry"><span id="mm"></span>/<span id="yy"></span></span>
    </div>
    <div class="dk-card__group dk-card__group--cvv">
      <span class="dk-card__label">CVV</span>
      <span id="cvv"></span>
    </div>
  </div>
</div>
```

Into each value node, the SDK injects its own field markup:

```html theme={null}
<!-- e.g. inside .dk-card__pan -->
<span class="dk-field" data-dk-field="pan">
  <span class="dk-field__masked">•••• •••• •••• 4242</span>  <!-- shown when masked -->
  <span class="dk-field__frame" hidden>…iframe…</span>        <!-- shown when revealed -->
</span>
<span class="dk-live" aria-live="polite"></span>              <!-- reveal/mask announcements (pan only) -->
```

`.dk-field__masked` holds the placeholder glyphs, and `.dk-field__frame` hosts the value frame. The SDK toggles their `hidden` attribute as the card reveals and re-masks.

The swap happens at the **start** of a reveal: the frame side becomes visible while the value frames are still loading. The default stylesheet paints an empty visible host as a quiet loading pulse (`.dk-field__frame:not([hidden]):empty`). Restyle that selector to change the loading look. A failed reveal swaps back to the masked side. The visually-hidden `.dk-live` region announces transitions to screen readers.

### The style contract

These class names, the `data-dk-field` attribute, and the custom properties below are a **public contract covered by semantic versioning**. They will not change without a major release. Target them freely from your own CSS.

| Selector | Role |
| - | - |
| `.dk-card` | The card face. Holds the theming custom properties. |
| `.dk-card__pan` | PAN value slot. |
| `.dk-card__row` / `.dk-card__group` / `.dk-card__group--cvv` | Layout for the expiry and CVV row. |
| `.dk-card__label` | The "VALID THRU" and "CVV" captions. |
| `.dk-card__expiry` | Wraps the month and year value nodes. |
| `.dk-field` / `.dk-field__masked` / `.dk-field__frame` | SDK-injected per-field wrapper, masked glyphs, and value-frame host. |
| `.dk-live` | Visually-hidden ARIA live region. |

## Custom markup (the headless hook)

Rendering your own face with `useCardDetails` makes the per-field internals (`.dk-field__masked` and `.dk-field__frame`) part of *your* stylesheet. Four of those rules are load-bearing:

1. **Never `display:none` a hidden frame host.** Browsers deny rendering to `display:none` subtrees, and the card-data processor's frames only finish mounting once they render. Hide with `visibility: hidden` and keep the box, as the default stylesheet does.
2. **Explicitly size visible frame hosts — with slack.** An auto-sized host lets the frame fall back to the browser's default iframe dimensions (300×150). An exactly-fitted one lets fractional rounding overflow the embedded page, and its own scrollbars render over the digits. Give every frame a text-line height and a per-field width, each a few pixels larger than the text needs.
3. **Host height must cover the captured line-height.** The frames copy each host's computed text styles (see below). If the host box is shorter than that line box, the digits clip at the bottom.
4. **Re-anchor inline placements while revealed.** A revealed `.dk-field` has no text baseline, because the masked glyphs are hidden. A widget placed inline next to text therefore aligns by its bottom edge, and the digits ride high. Center it on the line instead: `.dk-field:has(.dk-field__masked[hidden]) { vertical-align: middle }`.

The SDK-injected `.dk-live` announcer hides itself, inline through the CSSOM, which content security policies do not block. Custom stylesheets need no rule for it. Frame hosts likewise declare `color-scheme: light` on themselves, matching the frame documents, so browsers keep the frames transparent inside dark-scheme apps. No rule is needed, and there is no white backdrop behind the digits.

Per-field text metrics come from your CSS on the mount targets — each frame captures its host's computed styles — while the `theme` option carries the uniform brand values (color, family, weight, spacing) into every frame. Set sizes per field in CSS. Set the shared look once in `theme`.

## Restyle with CSS

The quickest reskin is to override the custom properties on `.dk-card`:

| Property | Default | Controls |
| - | - | - |
| `--dk-card-bg` | `#000000` | Card background color. |
| `--dk-card-fg` | `#ffffff` | Foreground (label and value) color on the face. |
| `--dk-card-muted` | `#808483` | Muted caption color. |
| `--dk-card-radius` | `8px` | Corner radius. |

```css theme={null}
.dk-card {
  --dk-card-bg: #0b1f3a;
  --dk-card-fg: #ffffff;
  --dk-card-muted: #93a4bd;
  --dk-card-radius: 12px;
}
```

Need more than the properties expose? Re-declare any rule on the recipe's classes. The default stylesheet is a set of sensible defaults, not a locked black box, so you can change the dimensions, fonts, or layout without forking it.

<Note>
  The face styling above lives in **your** DOM. It does not reach inside the revealed value frames. For those, use the [`theme` option](#the-theme-option).
</Note>

## The `theme` option

The revealed values render inside frames that the SDK cannot restyle from the outside. To make them match your face, pass a `theme` when you create the handle. Its keys are text-style overrides that are forwarded into the value frames:

```ts theme={null}
const card = cards.cardDetails({
  last4: '4242',
  theme: {
    color: '#ffffff',
    fontFamily: 'Barlow, system-ui, sans-serif',
    fontSize: '28px',
    fontWeight: '600',
    letterSpacing: '0.06em',
  },
});
```

In React, pass the same object as the `theme` prop.

Inside the frames, `Barlow` resolves only on systems that have it installed. The frames load no webfonts (see the constraints below), so otherwise the `system-ui` fallback carries the look. List your brand font first if you want it where available, but choose a fallback you are happy to ship.

`CardTheme` accepts exactly these keys. Each one maps to the CSS property of the same name:

| `CardTheme` key | CSS property propagated into the value frame |
| - | - |
| `color` | `color` |
| `fontFamily` | `font-family` |
| `fontSize` | `font-size` |
| `fontWeight` | `font-weight` |
| `letterSpacing` | `letter-spacing` |

Two constraints apply to the values:

* **They must be concrete.** The values are serialized into the frame documents, where your page's CSS custom properties do not exist. A `var()` reference is rejected, and the handle throws an `invalid_theme` error at creation. Resolve design tokens to literal values before you pass them. In React, the handle is created while mounting, so the throw surfaces as a component error that your nearest error boundary catches.
* **Fonts must be available inside the frames.** The frames load no webfonts, so name widely-available families with a generic fallback, rather than your brand's loaded font: `'Courier New, monospace'`, not a webfont-only family.

<Note>
  **Fidelity note.** These five text properties are the styles that carry into the value frames. Dakota confirms the exact rendering inside the frames against the live card-data processor **for each release**. Treat the propagated result as verified for the version you install, and check it again after you upgrade if pixel-exact matching matters to you.
</Note>

## Internationalization

The masked face and its screen-reader announcements use a small set of strings that you can override for each handle, through the `strings` option. Any key you omit falls back to its default.

```ts theme={null}
const card = cards.cardDetails({
  last4: '4242',
  strings: {
    validThru: 'VÁLIDO HASTA',
    cvvLabel: 'CVV',
    panPlaceholder: '•••• •••• •••• {last4}',
    revealedAnnouncement: 'Detalles de la tarjeta visibles',
    maskedAnnouncement: 'Detalles de la tarjeta ocultos',
  },
});
```

| `strings` key | Default | Used for |
| - | - | - |
| `validThru` | `VALID THRU` | The expiry caption. |
| `cvvLabel` | `CVV` | The CVV caption. |
| `panPlaceholder` | `•••• •••• •••• {last4}` | Masked PAN text. `{last4}` is replaced with the `last4` option, or with `••••`. |
| `revealedAnnouncement` | `Card details revealed` | Announced when the card reveals. |
| `maskedAnnouncement` | `Card details hidden` | Announced when the card re-masks. |

In React, the `<CardDetails>` component reads `validThru` and `cvvLabel` for the on-face captions, and passes the full `strings` object through to the core for the placeholder and the announcements.
