beta

Vector Field <mv-vector-field>

Campos compactos X/Y, X/Y/Z o W/H para inspectores: cada eje es un spinbutton en el que puedes escribir, avanzar por pasos con el teclado o arrastrar su letra para ajustarlo, con una línea fina que muestra dónde se sitúa el valor en su rango y un bloqueo de relación de aspecto opcional. El valor “x,y” está asociado a formularios.

CategoríaFormularios
TipoWeb Component (<mv-vector-field>)
Estadobeta
Keywordsvector, xy, xyz, size, aspect-ratio, lock, scrub, spinbutton, inspector, form-associated, editor

When to use

  • An inspector edits a position as X/Y or X/Y/Z values side by side
  • Width and height must be edited with a lock that keeps the aspect ratio
  • A form submits several related numbers as one comma-separated value

Avoid when

  • Only one number is edited → use Number Field instead
  • The audience is non-technical and expects labeled regular fields → use Number Field instead

Instalación

node scripts/add.mjs vector-field --out ./src/marvelous

Agente de IA con el servidor MCP de Marvelous UI: install_components({ slugs: ["vector-field"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Archivos copiados (dependencias incluidas): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/form.js, components/vector-field/vector-field.js, components/vector-field/vector-field.css.

Uso

Marcado de referencia: parte de él y personalízalo con atributos, data-* y variables CSS:

<div id="vec-demo">
  <style>
    #vec-demo { display: flex; flex-wrap: wrap; gap: 1.5rem; align-items: center; justify-content: center; width: 100%; }
    #vec-demo .vfd-stage { position: relative; width: 18rem; height: 15rem; overflow: hidden; border-radius: var(--mv-radius-xl); background: radial-gradient(circle at 1px 1px, color-mix(in oklch, var(--mv-fg) 12%, transparent) 1px, transparent 0) 0 0 / 12px 12px, var(--mv-bg); border: 1px solid var(--mv-border); perspective: 600px; }
    #vec-demo .vfd-card { position: absolute; left: 0; top: 0; display: grid; place-items: center; border-radius: 14px; color: #fff; font: 600 .7rem var(--mv-font-mono); background: linear-gradient(135deg, oklch(0.72 0.16 250), oklch(0.6 0.2 285)); box-shadow: 0 0 0 1px oklch(1 0 0 / .15) inset, 0 14px 30px -14px oklch(0.4 0.2 270 / .7); outline: 1px dashed color-mix(in oklch, var(--mv-accent) 70%, transparent); outline-offset: 4px; }
    #vec-demo .vfd-panel { display: grid; gap: .5rem; width: 17rem; padding: .75rem; border: 1px solid var(--mv-border); border-radius: var(--mv-radius-lg); background: var(--mv-surface); box-shadow: var(--mv-shadow-sm); }
    #vec-demo .vfd-head { margin: 0; font: 600 .7rem var(--mv-font-sans); letter-spacing: .06em; text-transform: uppercase; color: var(--mv-fg-subtle); }
    #vec-demo .vfd-row { display: grid; gap: .3rem; font-size: .72rem; color: var(--mv-fg-muted); }
  </style>
  <div class="vfd-stage"><div class="vfd-card" id="vec-demo-card"></div></div>
  <div class="vfd-panel" role="group" aria-label="Inspector">
    <p class="vfd-head">Layout</p>
    <div class="vfd-row"><span>Position</span>
      <mv-vector-field id="vec-demo-pos" label="Position" name="position" axes="X,Y" unit="px" value="64,48" min="0,0" max="240,200"></mv-vector-field>
    </div>
    <div class="vfd-row"><span>Size</span>
      <mv-vector-field id="vec-demo-size" label="Size" name="size" axes="W,H" axis-labels="Width,Height" unit="px" value="144,96" min="24" max="280" linkable linked></mv-vector-field>
    </div>
    <div class="vfd-row"><span>3D rotation</span>
      <mv-vector-field id="vec-demo-rot" label="3D rotation" name="rotation" axes="X,Y,Z" unit="°" value="0,-18,0" min="-180" max="180"></mv-vector-field>
    </div>
  </div>
</div>
<script type="module">
  const $ = (id) => document.getElementById(`vec-demo-${id}`);
  const apply = () => {
    const [x, y] = $("pos").values, [w, hh] = $("size").values, [rx, ry, rz] = $("rot").values;
    const c = $("card");
    c.style.width = `${w}px`;
    c.style.height = `${hh}px`;
    c.style.transform = `translate(${x}px, ${y}px) rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`;
    c.textContent = `${Math.round(w)} × ${Math.round(hh)}`;
  };
  document.getElementById("vec-demo").addEventListener("mv-input", apply);
  customElements.whenDefined("mv-vector-field").then(() => requestAnimationFrame(apply));
</script>

API

Attributes

NameTipoDefaultDescription
valuestringComma-separated values: “120,48”. Restored on reset.
axesstringX,YShort axis labels (2 to 4), shown in each handle.
axis-labelsstringAccessible axis names (“Width,Height”); otherwise “<label> <axis>”.
labelstringGroup name (role=group).
unit / min / max / stepstringOne value for all axes or one per axis (“0,0,-100”). Values are clamped to min/max; a bounded axis shows its position as a hairline.
precisionnumberstep decimals + 1Decimals kept, for all axes.
linkablebooleanShows the aspect-ratio lock button.
linkedbooleanAspect ratio locked: changing one axis scales the others (ratio captured when locking).
name / disabled / readonlyStandard behavior.

Properties

NameTipoDescription
valuestring“x,y[,z]”.
valuesnumber[]Numeric values (read/write). Assignable before the element is defined.
fieldsHTMLInputElement[]The generated axis inputs (role=spinbutton), read-only.
strings{ lock }Default texts, for translation (“Lock aspect ratio”). Assignable before the element is defined.

Methods

NameDescription
focus()Focuses the first axis.

Events

NameDescription
mv-inputOn every value change (drag, arrow keys, committed typing). detail: { value, values, axis }.
mv-changeOn commit: drag release, arrow key, Enter, blur. detail: { value, values, axis }. Native input/change events of the inner inputs do not bubble out.
mv-linkThe lock was toggled. detail: { linked }.

CSS classes

NameDescription
mv-vector-field-axisOne axis cell; data-scrubbing while its letter is dragged.
mv-vector-field-handle / -input / -unit / -meterGenerated parts: draggable letter, input, unit suffix, range hairline.
mv-vector-field-linkLock button (aria-pressed).

CSS variables

NameDefaultDescription
--mv-vector-field-width100%Group width.
--mv-vector-field-gapvar(--mv-space-1)Gap between fields.

Accessibility

role="group" named by label. Each axis is a text input with role="spinbutton", aria-valuenow/min/max and an aria-valuetext that includes the unit (“64 px”), named by axis-labels or “<label> <axis>”. Keyboard: ↑/↓ step (Shift ×10, Alt ×0.1), PageUp/PageDown ×10, Home/End jump to min/max, type a value then Enter or Tab to commit, Escape to revert the typing or cancel a drag. An unreadable value is flagged with aria-invalid until fixed or left. Dragging the axis letter is a pointer shortcut only (no pointer lock, system cursor kept). The lock is a toggle button (aria-pressed) named “Lock aspect ratio”. Forced colors keep borders, focus, the range hairline and the pressed lock visible.

Esta página se tradujo con IA. Informar de un problema de traducción