diff --git a/packages/theme/dist/v4/globals.css b/packages/theme/dist/v4/globals.css
index 060d77ad1..2557c50d8 100644
--- a/packages/theme/dist/v4/globals.css
+++ b/packages/theme/dist/v4/globals.css
@@ -669,6 +669,9 @@
--spacing-sm: 0.75rem;
--spacing-xs: 0.5rem;
--spacing-xxs: 0.25rem;
+ --z-input-field: 1;
+ --z-input-popup: 10;
+ --z-input-overlay: 1100;
--text-big-number-md-font-size: 1.25rem;
--text-big-number-md-line-height: 1.25;
--text-big-number-md-font-weight: 400;
diff --git a/packages/theme/dist/v4/globals.scss b/packages/theme/dist/v4/globals.scss
index 060d77ad1..2557c50d8 100644
--- a/packages/theme/dist/v4/globals.scss
+++ b/packages/theme/dist/v4/globals.scss
@@ -669,6 +669,9 @@
--spacing-sm: 0.75rem;
--spacing-xs: 0.5rem;
--spacing-xxs: 0.25rem;
+ --z-input-field: 1;
+ --z-input-popup: 10;
+ --z-input-overlay: 1100;
--text-big-number-md-font-size: 1.25rem;
--text-big-number-md-line-height: 1.25;
--text-big-number-md-font-weight: 400;
diff --git a/packages/theme/src/scripts/build-tokens.mjs b/packages/theme/src/scripts/build-tokens.mjs
index 70004177d..187111bc6 100644
--- a/packages/theme/src/scripts/build-tokens.mjs
+++ b/packages/theme/src/scripts/build-tokens.mjs
@@ -34,6 +34,7 @@ import { compileThemeCss, compileThemeVars } from './compile-theme.js';
import { containersData } from '../tokens/semantic/containers.data.js';
import { spacingsData } from '../tokens/semantic/spacings.data.js';
import { textsData } from '../tokens/semantic/texts.data.js';
+import { zIndicesData } from '../tokens/semantic/z-indices.data.js';
const BREAKPOINT_ORDER = ['sm', 'md', 'lg', 'xl', '2xl'];
@@ -91,6 +92,7 @@ const buildFlatModel = () => ({
primitives: flattenPrimitives(),
containers: flattenSingleValue(containersData, (k) => `--container-${k}`),
spacings: flattenSingleValue(spacingsData, (k) => `--${k}`),
+ zIndices: flattenSingleValue(zIndicesData, (k) => `--${k}`),
texts: flattenBundle(textsData),
});
@@ -295,6 +297,7 @@ const emitCssV4 = () => {
...rootPrimitiveVars,
...(m.containers._ || {}),
...(m.spacings._ || {}),
+ ...(m.zIndices._ || {}),
...(m.texts._ || {}),
};
@@ -303,6 +306,7 @@ const emitCssV4 = () => {
const merged = {
...(m.containers[bp] || {}),
...(m.spacings[bp] || {}),
+ ...(m.zIndices[bp] || {}),
...(m.texts[bp] || {}),
};
if (Object.keys(merged).length === 0) continue;
diff --git a/packages/theme/src/tokens/semantic/z-indices.data.js b/packages/theme/src/tokens/semantic/z-indices.data.js
new file mode 100644
index 000000000..ee31fb136
--- /dev/null
+++ b/packages/theme/src/tokens/semantic/z-indices.data.js
@@ -0,0 +1,30 @@
+/**
+ * Declarative z-index tokens.
+ *
+ * Intra-input stacking:
+ *
+ * - `z-input-field` → native field / interactive foreground (e.g. an
+ * `` or trigger button) sitting above the
+ * input's own background surface, icons, and
+ * adornments so keyboard/mouse focus is never
+ * hijacked by a decorative sibling.
+ * - `z-input-popup` → inline (non-teleported) overlays anchored to the
+ * input — dropdown menus, option lists, popovers —
+ * that must sit above surrounding form content but
+ * within the current stacking context.
+ * - `z-input-overlay` → teleported overlays (rendered under `
` via
+ * ``) that must clear application chrome
+ * such as drawers, modals, and fixed headers.
+ *
+ * Values are intentionally small and well-spaced to make future additions
+ * obvious. Consume as `z-[var(--z-input-*)]` in components — do not
+ * introduce ad-hoc numeric z-index in input `.vue` files.
+ */
+
+export const zIndicesData = {
+ 'z-input-field': { _: '1' },
+ 'z-input-popup': { _: '10' },
+ 'z-input-overlay': { _: '1100' },
+};
+
+export default { zIndicesData };
diff --git a/packages/webkit/catalog.json b/packages/webkit/catalog.json
index accdbce63..063a1244c 100644
--- a/packages/webkit/catalog.json
+++ b/packages/webkit/catalog.json
@@ -671,7 +671,10 @@
"--width-md",
"--width-sm",
"--width-xl",
- "--width-xs"
+ "--width-xs",
+ "--z-input-field",
+ "--z-input-overlay",
+ "--z-input-popup"
],
"typography": [
"text-big-number-lg",
@@ -1408,6 +1411,11 @@
"--width-sm",
"--width-xl",
"--width-xs"
+ ],
+ "z": [
+ "--z-input-field",
+ "--z-input-overlay",
+ "--z-input-popup"
]
},
"animations": [
diff --git a/packages/webkit/src/components/inputs/chip/chip.vue b/packages/webkit/src/components/inputs/chip/chip.vue
index ce2bf8ca7..dfaef57b8 100644
--- a/packages/webkit/src/components/inputs/chip/chip.vue
+++ b/packages/webkit/src/components/inputs/chip/chip.vue
@@ -109,7 +109,9 @@
@click="onClick"
@keydown="onKeydown"
>
-
+
@@ -145,21 +145,21 @@
:aria-required="required || undefined"
:data-testid="testId"
v-bind="passthroughAttrs"
- class="relative z-[1] w-full min-w-0 border-0 bg-transparent outline-none px-[var(--spacing-md)] text-label-sm text-[var(--text-default)] placeholder:text-[var(--text-muted)] disabled:cursor-not-allowed disabled:text-[var(--text-disabled)] read-only:cursor-default [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
+ class="relative z-[var(--z-input-field)] w-full min-w-0 border-0 bg-transparent outline-none px-[var(--spacing-md)] text-label-sm text-[var(--text-default)] placeholder:text-[var(--text-muted)] disabled:cursor-not-allowed disabled:text-[var(--text-disabled)] read-only:cursor-default [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
@input="handleInput"
@change="handleChange"
/>
@@ -167,7 +167,7 @@
diff --git a/packages/webkit/src/components/inputs/input-text/input-text.vue b/packages/webkit/src/components/inputs/input-text/input-text.vue
index 7e62f811c..9f142fe8e 100644
--- a/packages/webkit/src/components/inputs/input-text/input-text.vue
+++ b/packages/webkit/src/components/inputs/input-text/input-text.vue
@@ -114,7 +114,7 @@
:aria-invalid="invalid || undefined"
:aria-required="required || undefined"
:data-testid="testId"
- class="relative z-[1] w-full min-w-0 border-0 bg-transparent p-0 outline-none text-label-sm text-[var(--text-default)] placeholder:text-[var(--text-muted)] disabled:cursor-not-allowed disabled:text-[var(--text-disabled)] read-only:cursor-default"
+ class="relative z-[var(--z-input-field)] w-full min-w-0 border-0 bg-transparent p-0 outline-none text-label-sm text-[var(--text-default)] placeholder:text-[var(--text-muted)] disabled:cursor-not-allowed disabled:text-[var(--text-disabled)] read-only:cursor-default"
v-bind="passthroughAttrs"
@input="handleInput"
/>
diff --git a/packages/webkit/src/components/inputs/multi-select/multi-select-content/multi-select-content.vue b/packages/webkit/src/components/inputs/multi-select/multi-select-content/multi-select-content.vue
index 39ceb0169..d8288b925 100644
--- a/packages/webkit/src/components/inputs/multi-select/multi-select-content/multi-select-content.vue
+++ b/packages/webkit/src/components/inputs/multi-select/multi-select-content/multi-select-content.vue
@@ -137,7 +137,7 @@
:data-state="ctx.open.value ? 'open' : 'closed'"
:class="attrs.class"
:style="positionStyle"
- class="fixed z-50 flex max-h-[20rem] flex-col overflow-hidden rounded-[var(--shape-elements)] border border-[var(--border-default)] bg-[var(--bg-surface-raised)] shadow-[var(--shadow-xs)]"
+ class="fixed z-[var(--z-input-overlay)] flex max-h-[20rem] flex-col overflow-hidden rounded-[var(--shape-elements)] border border-[var(--border-default)] bg-[var(--bg-surface-raised)] shadow-[var(--shadow-xs)]"
@keydown="onKeydown"
>