Skip to content

[TextField] Lift hardcoded padding and InputLabel transform values into theme tokens for customization #48541

@NotYoojun

Description

@NotYoojun

Summary

The TextField / OutlinedInput / Input components have several spacing values written as literal numbers inside their styled() definitions, rather than being exposed as theme tokens. The same is true of InputLabel, whose transform: translate(x, y) values are implicitly coupled to the input's padding but are themselves hardcoded in a separate file.

In OutlinedInput.js the relevant literals include:

  • paddingLeft: 14 (when startAdornment is present)
  • paddingRight: 14 (when endAdornment is present)
  • padding: '16.5px 14px' (multiline, default)
  • padding: '8.5px 14px' (multiline, size="small")
  • padding: '16.5px 14px' / padding: '8.5px 14px' on OutlinedInputInput

In InputLabel.js the matching label positions are also literal:

  • transform: 'translate(14px, 16px) scale(1)' (outlined, default)
  • transform: 'translate(14px, 9px) scale(1)' (outlined, small)
  • transform: 'translate(14px, -9px) scale(0.75)' (outlined, shrink)
  • transform: 'translate(12px, 16px) scale(1)' (filled, default), and corresponding small/shrink variants
  • maxWidth: 'calc(100% - 24px)', maxWidth: 'calc(133% - 32px)' (outlined)

The horizontal 14px translate in the outlined InputLabel only aligns with the field because it happens to match the 14px horizontal padding of OutlinedInput. The two files maintain this contract by hand, and the constants 24px / 32px / 133% further depend on it.

I'd like these spacing values to be lifted into the theme so that they're a public, documented surface — ideally so that overriding the input's horizontal padding automatically propagates to the label's transform.

Internally OutlinedInput would derive its padding from those tokens, and InputLabel would derive the X component of its translate from the same paddingX (and the Y component from paddingY plus the label's font metrics). Today both numbers are written by hand in two files.

The same pattern repeats in FilledInput.js (e.g. padding: '25px 12px 8px', padding: '21px 12px 4px' for small, plus adornment-specific overrides) and to a lesser extent in Input.js and InputBase.js. Each file maintains its own set of literals that need to stay coordinated with InputLabel's filled / standard transform blocks, so the problem is structural across the input family rather than specific to OutlinedInput. Note that startAdornment / endAdornment introduce yet another layer: they don't tweak the existing padding token, they overwrite it with their own hardcoded 14px, so any override has to be re-applied conditionally for the adornment case.

I'm aware styleOverrides lets you reach these styles in principle, so this isn't strictly "impossible". But because the values live inside variants: blocks, are spread across two component slots and two files, and are tied together by an undocumented contract, doing it correctly requires re-deriving every magic number by hand.

Examples

The minimal failure case: override OutlinedInput to use a non-default horizontal padding, e.g.

MuiOutlinedInput: {
  styleOverrides: {
    root: { '& .MuiOutlinedInput-input': { padding: '10px 10px' } },
  },
},

Now the shrunk InputLabel floats at translate(14px, -9px) while the notched outline cutout starts at 10px from the left — the label sits inside-but-misaligned. To fix it you have to additionally override MuiInputLabel.styleOverrides.outlined with a hand-computed transform, and repeat the exercise for size="small" and the shrink state.

Material Design's own text-field spec defines internal spacing semantically (edge insets, baseline alignment) rather than as fixed pixels, so making these configurable is, if anything, closer to the spec than the current hardcoded numbers.

Motivation

The built-in size: 'small' | 'medium' only gives two discrete density steps. Real-world design systems built on top of Material UI commonly need:

  • Redefining the default medium and small themselves, not just adding extra steps — so that the baseline <TextField /> already matches the host design system without consumers having to opt into a custom size everywhere.
  • Adding further density steps (e.g. "compact" or "comfortable") beyond small/medium when needed.
  • Slightly adjusted horizontal padding so text fields align with adjacent custom-sized buttons, selects, or chips.
  • Density tweaks for CJK content, where body line-height and x-height differ enough from the Roboto-tuned defaults to make the stock paddings feel off.

Currently, achieving any of this means overriding MuiOutlinedInput.styleOverrides.root, MuiOutlinedInput.styleOverrides.input, and MuiInputLabel.styleOverrides.outlined in sync, and re-deriving every constant (16.5, 8.5, 14, 9, -9, 24, 32, 133%) by hand. None of these are documented as a public API, which means they could legitimately change between minor versions.

Promoting them to named theme tokens would:

  1. Reduce a multi-component override cascade to a single token change.
  2. Make the implicit input-padding <-> label-transform contract explicit and documented.
  3. Bring text-field density into line with the rest of MUI's theming surface (shape, spacing, typography).

Search keywords: OutlinedInput padding hardcoded, InputLabel transform hardcoded, TextField density customization, InputLabel alignment, theme spacing tokens

Metadata

Metadata

Assignees

No one assigned

    Labels

    scope: text fieldChanges related to the text field.type: enhancementIt’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature.waiting for 👍Waiting for upvotes. Open for community feedback and needs more interest to be worked on.
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions