# Form control sizing, corrected typings, and a dozen fixes

> Source: https://design.nanatec.co.ke/blog/form-controls-typings-and-fixes/
> One sizing system across the form controls, framework typings that finally match the runtime, new input constraints, and twelve fixes — including three that change existing behaviour.

<p style="color: var(--sl-color-gray-3); font-size: 0.9rem; margin-top: -0.5rem;">
  <time datetime="2026-08-15">August 15, 2026</time> · Release
</p>

This release changes how the form controls look, corrects the generated framework
typings, and fixes a dozen defects. Three items change existing behaviour — read
**Things that change** before upgrading.

## Things that change

**Form controls are a different size.** `nana-input`, `nana-textarea`,
`nana-select` and `nana-combobox` now share one scale. At the default `md` size a
field is **44px tall** (was 42px), body text is **14px** (was 16px), and the
corner radius is **8px** (was 6px). Layouts that hard-coded the old dimensions
will shift.

| | xs | sm | md | lg |
|---|---|---|---|---|
| Field height | 28px | 36px | **44px** | 52px |
| Text | 12px | 13px | **14px** | 16px |

Every dimension is overridable, so you can pin the old rhythm if you need to:

```css
nana-input { --nana-input-height: 42px; --nana-input-radius: 6px; }
```

**`spellcheck="false"` now actually disables spellchecking** on `nana-textarea`.
It previously enabled it — the attribute was read as a boolean, so any value at
all turned it on. If you wrote `spellcheck="false"` and relied on the old
behaviour, remove the attribute.

**Group components emit one event, not two.** `nana-radio-group` and
`nana-checkbox-group` previously let the child control's `nana-change` escape
alongside the group's own, so a listener on the group fired twice per click with
two different `detail` shapes. If you added a de-duplication guard, you can
remove it.

## New

**`nana-input` accepts the numeric and keyboard attributes.** `min`, `max`,
`step`, `pattern` and `inputmode` now reach the underlying field. This also makes
`rangeUnderflow`, `rangeOverflow`, `stepMismatch` and `patternMismatch` available
through constraint validation — previously unreachable.

```html
<nana-input label="Amount" type="number" min="0" max="1000" step="0.5" inputmode="decimal" placeholder="0.00" style="max-width:22rem"></nana-input>
```

**Slotted icons get a reserved zone.** A `prefix` or `suffix` icon sits in a fixed
20px zone — 12px from the border, 8px from the text — so the text starts in the
same place regardless of the icon's own width, and icons no longer sit flush
against the border or the text.

```html
<nana-input label="Search" placeholder="Search…" style="max-width:22rem">
  <nana-icon slot="prefix" name="search"></nana-icon>
</nana-input>
```

Tune it per instance or globally with `--nana-input-affix-inset`,
`--nana-input-affix-gap`, `--nana-input-affix-zone` and
`--nana-input-affix-icon-size`.

**`nana-textarea`'s `max-height` works.** Auto-grow stops at the given height and
the field scrolls, instead of growing without limit.

**The tab indicator is adjustable** through `--nana-tab-group-indicator-size`.

## Typings

The generated typings for React, Vue, Svelte and JSX were wrong for every
multi-word attribute — `helpertext` instead of `helper-text`, `stickyheader`
instead of `sticky-header`, and so on for all 65 kebab-case attributes. They are
now correct, and both forms are typed: the attribute (`sticky-header`) and the
property (`stickyHeader`), since JSX writes one and React 19 sets the other.

If you maintain hand-written JSX shims to work around this, you can drop them in
favour of `@nana-tec/ui-components/jsx`.

Two components also gained the React wrappers they were missing:
`NanaSelect` and `NanaTextarea` from `@nana-tec/ui-components/react`.

Editors read the same data, so attribute autocomplete now works for
`<nana-*>` tags.

## Fixed

- `<nana-format-date>` and `<nana-relative-time>` rendered nothing when `date`
  was a millisecond timestamp. Both now accept epoch values.
- `nana-combobox` never showed its "No results found" state.
- A disabled `nana-select` opened on <kbd>↓</kbd>, <kbd>↑</kbd>,
  <kbd>Enter</kbd> and <kbd>Space</kbd>.
- Slotted prefix/suffix spacing was silently removed by CSS resets — most
  visibly Tailwind's Preflight, which every consuming app runs. Icons appeared
  flush against the border and the text.
- `nana-icon` compressed below its declared size inside a tight flex row. If you
  added `flex-shrink: 0` or a `shrink-0` class at call sites, it is no longer
  needed.
- The `nana-tab` count badge was unreadable in dark mode (1.94:1 contrast).
- The tab indicator was a half-pixel bar that animated in from zero width on
  every mount and floated above the divider rather than sitting on it.
- Duplicate registration of a component — from two copies of the package —
  failed silently. It now warns, naming the likely cause.

## Accessibility

The focus ring failed WCAG SC 1.4.11 in both themes — 2.16:1 on light, 1.14:1 on
dark, where it was effectively invisible. It now clears the 3:1 requirement on
both: **3.83:1** and **5.16:1**. Muted and subtle text moved from 2.52:1 to
4.74:1.

If you set `--nana-focus-ring` yourself, check it against the same threshold.

## Documentation

Every component page now generates its API reference from the package itself, so
properties, events, slots, CSS parts and custom properties stay in step with the
code — with real descriptions in place of the empty column the tables used to
carry.

## Upgrading

Nothing requires a code change unless you are in one of the three cases under
**Things that change**. The quickest check is a visual pass over dense forms,
where the 42px → 44px field height and the type change are most noticeable.
