DesignPass.dev

ui

Free

IsometricField

A text input rendered as an isometric slab — the natural companion to IsometricButton, sharing its camera. The depth edge lights up in your accent color on focus.

// preview

// settings

Thickness (px)
Corner radius (px)
Font size (px)
advanced
Camera tilt X (deg)
Camera spin Z (deg)
Stand angle (deg)

// source

TSJS
/*!
 * IsometricField — a DesignPass.dev component by Ernest Liu
 * Docs & live playground: https://designpass.dev/components/isometric-field
 * MIT licensed — keep this notice in copies and adaptations.
 */
/* IsometricField — an input rendered as an isometric slab, designed as the
   companion of IsometricButton (same default camera angles). Unlike the
   button, the visible top face IS the real <input>: typing, caret, and
   selection must live on the rendered surface. The slab rests near the
   floor and its depth edge lights up in the accent color while focused. */

.iso-field-scene {
  --iso-field-rot-x: 54deg;
  --iso-field-rot-z: -42deg;
  /* Lean of the slab up out of the floor plane, hinged at its bottom
     edge (0 = flat, -90deg = fully upright). */
  --iso-field-stand: -45deg;
  --iso-field-thick: 10px;
  --iso-field-radius: 15px;
  --iso-field-gap: 2px;
  --iso-field-shift-y: 3px;
  --iso-field-surface: #f4f2f8;
  --iso-field-surface-focus: #ffffff;
  --iso-field-edge: #b9b2c6;
  --iso-field-accent: #a05cff;
  --iso-field-text-color: #000000;
  --iso-field-placeholder-color: rgba(0, 0, 0, 0.5);
  --iso-field-font-size: 1.25rem;
  --iso-field-padding-x: 20px;
  position: relative;
  display: block;
}

.iso-field-obj {
  position: absolute;
  inset: 0;
  /* Isometric camera — defaults match IsometricButton so both objects can
     share one 3D scene. */
  transform: translateY(var(--iso-field-shift-y)) rotateX(var(--iso-field-rot-x))
    rotateZ(var(--iso-field-rot-z));
  transform-style: preserve-3d;
}

/* Lean the slab up around its bottom edge. */
.iso-field-stand {
  position: absolute;
  inset: 0;
  transform-origin: center bottom;
  transform: rotateX(var(--iso-field-stand));
  transform-style: preserve-3d;
}

.iso-field-stand > * {
  position: absolute;
  inset: 0;
  border-radius: var(--iso-field-radius);
  transition:
    transform 0.25s cubic-bezier(0.22, 1, 0.36, 1),
    box-shadow 0.25s ease,
    border-color 0.25s ease;
}

/* Rounded side walls, faked with stacked slices. Spread-only (no blur)
   shadows fill the gaps between slices while keeping the edge crisp. */
.iso-field-side {
  background: var(--iso-field-edge);
  transform: translateZ(
    calc(var(--iso-field-gap) + var(--iso-field-thick) * var(--i) / 10)
  );
  box-shadow: 0 0 0 1px var(--iso-field-edge);
  transition:
    transform 0.25s cubic-bezier(0.22, 1, 0.36, 1),
    background-color 0.25s ease,
    box-shadow 0.25s ease;
  pointer-events: none;
}

/* Focused: the depth edge takes the accent color instead of a border. */
.iso-field-scene:focus-within .iso-field-side {
  background: var(--iso-field-accent);
  box-shadow: 0 0 0 1px var(--iso-field-accent);
}

.iso-field-top {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0 var(--iso-field-padding-x);
  border: 1px solid rgba(0, 0, 0, 0.12);
  background: var(--iso-field-surface);
  color: var(--iso-field-text-color);
  font-size: var(--iso-field-font-size);
  text-align: center;
  outline: none;
  transform: translateZ(calc(var(--iso-field-gap) + var(--iso-field-thick)));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
}

.iso-field-top::placeholder {
  color: var(--iso-field-placeholder-color);
}

.iso-field-top:focus {
  background: var(--iso-field-surface-focus);
}

.iso-field-top:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

@media (prefers-reduced-motion: reduce) {
  .iso-field-stand > * {
    transition: none;
  }
}


/*!
 * IsometricField — a DesignPass.dev component by Ernest Liu
 * Docs & live playground: https://designpass.dev/components/isometric-field
 * MIT licensed — keep this notice in copies and adaptations.
 */
"use client";

import type { CSSProperties, InputHTMLAttributes } from "react";
import "./IsometricField.css";

/**
 * Main knobs 90% of uses need. Everything else lives in `advanced`.
 * All values map to CSS custom properties consumed by IsometricField.css;
 * anything left undefined falls back to the stylesheet default.
 */
export interface IsometricFieldSettings {
  /** Slab thickness, e.g. "10px". */
  thickness?: string;
  /** Corner radius of all faces, e.g. "15px". */
  radius?: string;
  /** Input surface color. */
  surfaceColor?: string;
  /** Depth-edge color while focused (replaces a focus ring). */
  accentColor?: string;
  /** Input font size, e.g. "1.25rem". */
  fontSize?: string;
  /** Everything else — camera, lean, colors of individual parts. */
  advanced?: IsometricFieldAdvancedSettings;
}

export interface IsometricFieldAdvancedSettings {
  /** Isometric camera angles — match your IsometricButton's to share a scene. */
  rotateX?: string;
  rotateZ?: string;
  /** Lean of the slab out of the floor plane (0 = flat, "-90deg" = upright). */
  standAngle?: string;
  /** Height of the slab base above the floor, e.g. "2px". */
  gap?: string;
  /** Vertical offset of the whole scene, e.g. "3px". */
  shiftY?: string;
  /** Surface color while focused. */
  surfaceColorFocus?: string;
  /** Depth-edge color at rest. */
  edgeColor?: string;
  /** Input text / placeholder colors. */
  textColor?: string;
  placeholderColor?: string;
  /** Horizontal padding inside the input, e.g. "20px". */
  paddingX?: string;
}

const MAIN_VARS: Record<string, string> = {
  thickness: "--iso-field-thick",
  radius: "--iso-field-radius",
  surfaceColor: "--iso-field-surface",
  accentColor: "--iso-field-accent",
  fontSize: "--iso-field-font-size",
};

const ADVANCED_VARS: Record<string, string> = {
  rotateX: "--iso-field-rot-x",
  rotateZ: "--iso-field-rot-z",
  standAngle: "--iso-field-stand",
  gap: "--iso-field-gap",
  shiftY: "--iso-field-shift-y",
  surfaceColorFocus: "--iso-field-surface-focus",
  edgeColor: "--iso-field-edge",
  textColor: "--iso-field-text-color",
  placeholderColor: "--iso-field-placeholder-color",
  paddingX: "--iso-field-padding-x",
};

function settingsToStyle(settings: IsometricFieldSettings): CSSProperties {
  const style: Record<string, string> = {};
  const { advanced, ...main } = settings;

  for (const [key, value] of Object.entries(main)) {
    if (value === undefined) continue;
    style[MAIN_VARS[key]] = String(value);
  }
  for (const [key, value] of Object.entries(advanced ?? {})) {
    if (value === undefined) continue;
    style[ADVANCED_VARS[key]] = String(value);
  }
  return style as CSSProperties;
}

export interface IsometricFieldProps extends InputHTMLAttributes<HTMLInputElement> {
  /** Classes applied to the outer scene wrapper — size it here (the scene
   * has no intrinsic size), e.g. "w-80 h-14". */
  wrapperClassName?: string;
  /** Visual overrides; see IsometricFieldSettings. */
  settings?: IsometricFieldSettings;
}

/** Number of stacked slices used to fake the slab's rounded side walls.
 * Slices sit ~1px apart (thickness / count) with crisp, unblurred edge
 * shadows — fewer, farther-apart slices needed blurred shadows to hide
 * the gaps, which made the whole depth edge look fuzzy. */
const SIDE_LAYERS = 10;

/**
 * An input styled as an isometric slab — the natural companion to
 * IsometricButton (same default camera angles). Unlike the button, the
 * real <input> is the visible top face: text entry, caret, and selection
 * all need to live on the rendered surface. The slab rests near the floor
 * and its depth edge lights up in the accent color while focused.
 */
export default function IsometricField({
  wrapperClassName = "",
  settings,
  ...inputProps
}: IsometricFieldProps) {
  return (
    <span
      className={`iso-field-scene ${wrapperClassName}`}
      style={settings ? settingsToStyle(settings) : undefined}
    >
      <span className="iso-field-obj">
        <span className="iso-field-stand">
          {Array.from({ length: SIDE_LAYERS }, (_, i) => (
            <span
              key={i}
              className="iso-field-side"
              style={{ "--i": i } as CSSProperties}
              aria-hidden
            />
          ))}
          <input {...inputProps} className="iso-field-top" />
        </span>
      </span>
    </span>
  );
}

// install

npx shadcn@latest add "https://designpass.dev/r/IsometricField-TS-CSS.json"

Need the license details? Read the component license.

// updates

Know when new components drop

A short email when something new lands in the library. No noise, unsubscribe anytime.