Beta
Vector Field <mv-vector-field>
Kompakte Eingaben für X/Y, X/Y/Z oder W/H in Inspektoren: Jede Achse ist ein Spinbutton, in den du tippen, den du per Tastatur schrittweise ändern oder durch Ziehen an seinem Buchstaben scrubben kannst, mit einer Haarlinie, die zeigt, wo der Wert in seinem Bereich liegt, und einer optionalen Seitenverhältnis-Sperre. Der Wert „x,y“ ist formularfähig.
| Kategorie | Formulare |
|---|---|
| Typ | Web Component (<mv-vector-field>) |
| Status | Beta |
| Keywords | vector, 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
Installation
node scripts/add.mjs vector-field --out ./src/marvelousKI-Agent mit dem MCP-Server von Marvelous UI: install_components({ slugs: ["vector-field"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Kopierte Dateien (inklusive Abhängigkeiten): 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.
Verwendung
Referenz-Markup zum Starten und Anpassen mit Attributen, data-* und CSS-Variablen:
<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
| Name | Typ | Default | Description |
|---|---|---|---|
value | string | Comma-separated values: “120,48”. Restored on reset. | |
axes | string | X,Y | Short axis labels (2 to 4), shown in each handle. |
axis-labels | string | Accessible axis names (“Width,Height”); otherwise “<label> <axis>”. | |
label | string | Group name (role=group). | |
unit / min / max / step | string | One 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. | |
precision | number | step decimals + 1 | Decimals kept, for all axes. |
linkable | boolean | Shows the aspect-ratio lock button. | |
linked | boolean | Aspect ratio locked: changing one axis scales the others (ratio captured when locking). | |
name / disabled / readonly | Standard behavior. |
Properties
| Name | Typ | Description |
|---|---|---|
value | string | “x,y[,z]”. |
values | number[] | Numeric values (read/write). Assignable before the element is defined. |
fields | HTMLInputElement[] | The generated axis inputs (role=spinbutton), read-only. |
strings | { lock } | Default texts, for translation (“Lock aspect ratio”). Assignable before the element is defined. |
Methods
| Name | Description |
|---|---|
focus() | Focuses the first axis. |
Events
| Name | Description |
|---|---|
mv-input | On every value change (drag, arrow keys, committed typing). detail: { value, values, axis }. |
mv-change | On commit: drag release, arrow key, Enter, blur. detail: { value, values, axis }. Native input/change events of the inner inputs do not bubble out. |
mv-link | The lock was toggled. detail: { linked }. |
CSS classes
| Name | Description |
|---|---|
mv-vector-field-axis | One axis cell; data-scrubbing while its letter is dragged. |
mv-vector-field-handle / -input / -unit / -meter | Generated parts: draggable letter, input, unit suffix, range hairline. |
mv-vector-field-link | Lock button (aria-pressed). |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-vector-field-width | 100% | Group width. |
--mv-vector-field-gap | var(--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.