انتقل إلى المحتوى
uiify

Field

غلاف للتسمية وعنصر التحكم والخطأ.

المتصفحات
Chrome 123+Firefox 121+Safari 17.5+
الفئة
0
الحجم المضغوط
668 B
ميزانية التخطيط القسري
0 ms
ميزانية axe
0
متوافق مع SSR
نعم
العقد المنشور
بيئة الاختبار

المختبر

Field

Default

Your display name

Invalid

Password

Use at least eight characters.

Message

Up to 500 characters.

Disabled

Read only

import { Field } from "@gunkin/uiify";

export default function Example() {
  return (
    <Field.Root className="" id="email" required>
      <Field.Label className="" fieldId="email" required>
        Email
      </Field.Label>
      <Field.Control className="" fieldId="email" required as="input" type="email" />
      <Field.Description className="" fieldId="email">
        We will only use this for account notices.
      </Field.Description>
      <Field.Error className="" fieldId="email">
        Enter a valid email address.
      </Field.Error>
    </Field.Root>
  );
}
/* Text fields — Input and Textarea share the same surface. Invalid state keys
   off the [data-invalid] attribute the components emit. Textarea autosize uses
   the native field-sizing (Chrome 123+) when the component sets data-autosize. */

@layer components {
  :where([data-uiify-input], [data-uiify-textarea]) {
    display: block;
    inline-size: 100%;
    max-inline-size: 24rem;
    padding-block: var(--space-sm);
    padding-inline: var(--space-md);
    border: var(--border-width) solid var(--input);
    border-radius: var(--radius-md);
    corner-shape: squircle;
    background-color: var(--card);
    color: var(--foreground);
    font-size: var(--uiify-control-text);
    line-height: var(--leading-normal);
    transition: var(--transition-colors);
  }

  :where([data-uiify-input], [data-uiify-textarea])::placeholder {
    color: var(--muted-foreground);
  }

  :where([data-uiify-input], [data-uiify-textarea]):focus-visible {
    border-color: var(--primary);
  }

  :where([data-uiify-input], [data-uiify-textarea]):disabled,
  :where([data-uiify-input], [data-uiify-textarea])[data-disabled] {
    opacity: 0.5;
    cursor: not-allowed;
  }

  :where([data-uiify-input], [data-uiify-textarea])[data-invalid],
  :where([data-uiify-input], [data-uiify-textarea]):user-invalid {
    border-color: var(--destructive);
  }

  :where([data-uiify-input], [data-uiify-textarea])[data-invalid]:focus-visible {
    outline-color: var(--destructive);
  }

  [data-uiify-textarea] {
    min-block-size: 4lh;
    resize: block;
  }

  [data-uiify-textarea][data-autosize] {
    field-sizing: content;
    resize: none;
  }

  [data-uiify-field] {
    display: grid;
    gap: var(--space-sm);
    inline-size: 100%;
    max-inline-size: 24rem;
  }

  [data-uiify-field] [data-part="label"] {
    color: var(--foreground);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    line-height: var(--leading-normal);
  }

  [data-uiify-field] [data-part="label"][data-required]::after {
    content: " *";
    color: var(--destructive);
  }

  [data-uiify-field] [data-part="description"] {
    margin: 0;
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    line-height: var(--leading-normal);
  }

  [data-uiify-field] [data-part="error"] {
    margin: 0;
    color: var(--destructive);
    font-size: var(--text-xs);
    line-height: var(--leading-normal);
  }

  /* Shared presence fade when an error message is mounted. */
  @media (prefers-reduced-motion: no-preference) {
    [data-uiify-field] [data-part="error"] {
      transition: opacity var(--duration-fast) var(--ease-out);
    }

    @starting-style {
      [data-uiify-field] [data-part="error"] {
        opacity: 0;
      }
    }
  }
}
import { Field } from "@gunkin/uiify";

export default function Example() {
  return (
    <Field.Root
      className={
        "grid gap-sm [inline-size:100%] [max-inline-size:24rem] [&_[data-part=label]]:text-foreground [&_[data-part=" +
        "label]]:text-sm [&_[data-part=label]]:font-[number:var(--weight-medium)] [&_[data-part=label]]:[line-height" +
        ":var(--leading-normal)] [&_[data-part=label][data-required]::after]:content-[\"_*\"] [&_[data-part=label][dat" +
        "a-required]::after]:text-destructive [&_[data-part=description]]:m-0 [&_[data-part=description]]:text-muted" +
        "-foreground [&_[data-part=description]]:text-xs [&_[data-part=description]]:[line-height:var(--leading-norm" +
        "al)] [&_[data-part=error]]:m-0 [&_[data-part=error]]:text-destructive [&_[data-part=error]]:text-xs [&_[dat" +
        "a-part=error]]:[line-height:var(--leading-normal)] motion-safe:[&_[data-part=error]]:[transition:opacity_va" +
        "r(--duration-fast)_var(--ease-out)] motion-safe:starting:[&_[data-part=error]]:opacity-0"
      }
      id="email"
      required
    >
      <Field.Label
        className={
          "text-foreground text-sm font-[number:var(--weight-medium)] [line-height:var(--leading-normal)] data-[requ" +
          "ired]:after:content-[\"_*\"] data-[required]:after:text-destructive"
        }
        fieldId="email"
        required
      >
        Email
      </Field.Label>
      <Field.Control
        className="
          focus-visible:[outline:none]
          focus-visible:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_transparent)] block
          [inline-size:100%] [max-inline-size:24rem] py-sm px-md border-[length:var(--border-width)] border-solid
          border-input rounded-md [corner-shape:squircle] bg-card text-foreground [font-size:var(--uiify-control-text)]
          [line-height:var(--leading-normal)] [transition:var(--transition-colors)] placeholder:text-muted-foreground
          focus-visible:border-primary disabled:opacity-50 disabled:cursor-not-allowed data-[disabled]:opacity-50
          data-[disabled]:cursor-not-allowed data-[invalid]:border-destructive user-invalid:border-destructive
          data-[invalid]:focus-visible:outline-destructive
        "
        fieldId="email"
        required
        as="input"
        type="email"
      />
      <Field.Description
        className="m-0 text-muted-foreground text-xs [line-height:var(--leading-normal)]"
        fieldId="email"
      >
        We will only use this for account notices.
      </Field.Description>
      <Field.Error
        className="
          m-0 text-destructive text-xs [line-height:var(--leading-normal)] motion-safe:transition-opacity
          motion-safe:starting:opacity-0
        "
        fieldId="email"
      >
        Enter a valid email address.
      </Field.Error>
    </Field.Root>
  );
}
/* app.css */

@layer reset, ui, app, tokens, base, demo, components, utilities, theme;

@import "@gunkin/uiify/styles/reset";

@import "@gunkin/uiify/components/behavior.css";

@import "@gunkin/uiify/styles/tokens";

@import "@gunkin/uiify/styles/tailwind";

@import "tailwindcss/utilities.css";

@theme { --spacing: 0.25rem; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; }

@source inline('grid gap-sm [inline-size:100%] [max-inline-size:24rem] [&_[data-part=label]]:text-foreground [&_[data-part=label]]:text-sm [&_[data-part=label]]:font-[number:var(--weight-medium)] [&_[data-part=label]]:[line-height:var(--leading-normal)] [&_[data-part=label][data-required]::after]:content-["_*"] [&_[data-part=label][data-required]::after]:text-destructive [&_[data-part=description]]:m-0 [&_[data-part=description]]:text-muted-foreground [&_[data-part=description]]:text-xs [&_[data-part=description]]:[line-height:var(--leading-normal)] [&_[data-part=error]]:m-0 [&_[data-part=error]]:text-destructive [&_[data-part=error]]:text-xs [&_[data-part=error]]:[line-height:var(--leading-normal)] motion-safe:[&_[data-part=error]]:[transition:opacity_var(--duration-fast)_var(--ease-out)] motion-safe:starting:[&_[data-part=error]]:opacity-0 text-foreground text-sm font-[number:var(--weight-medium)] [line-height:var(--leading-normal)] data-[required]:after:content-["_*"] data-[required]:after:text-destructive focus-visible:[outline:none] focus-visible:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_transparent)] block py-sm px-md border-[length:var(--border-width)] border-solid border-input rounded-md [corner-shape:squircle] bg-card [font-size:var(--uiify-control-text)] [transition:var(--transition-colors)] placeholder:text-muted-foreground focus-visible:border-primary disabled:opacity-50 disabled:cursor-not-allowed data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed data-[invalid]:border-destructive user-invalid:border-destructive data-[invalid]:focus-visible:outline-destructive m-0 text-muted-foreground text-xs text-destructive motion-safe:transition-opacity motion-safe:starting:opacity-0');

@layer base { :root { --uiify-control-text: var(--text-sm); } @media (pointer: coarse) { :root { --uiify-control-text: var(--text-base); } } }

/* Authored animation and registered properties used by the Tailwind skin.
 * Visual rules belong in utility recipes; only at-rule definitions live here. */
@layer components {
  @keyframes uiify-presence-in {
    from {
      opacity: 0;
      scale: var(--presence-scale);
    }
  }
}

@layer components {
  @keyframes uiify-presence-out {
    to {
      opacity: 0;
      scale: var(--presence-scale);
    }
  }
}

@property --uiify-spinner-tail {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@property --uiify-spinner-length {
  syntax: "<angle>";
  inherits: false;
  initial-value: 36deg;
}

@layer components {
  @keyframes uiify-spinner-rotate {
    to {
      transform: rotate(360deg);
    }
  }
}

@layer components {
  @keyframes uiify-spinner-orbit {
    0% {
      --uiify-spinner-tail: 0deg;
      --uiify-spinner-length: 36deg;
    }

    50% {
      --uiify-spinner-tail: 90deg;
      --uiify-spinner-length: 324deg;
    }

    100% {
      --uiify-spinner-tail: 360deg;
      --uiify-spinner-length: 36deg;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-pulse {
    50% {
      opacity: 0.45;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-shimmer {
    to {
      background-position: -200% 0;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-reveal {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
}

@property --candle-body-height {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-body-top {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --candle-slot {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 100%;
}

@property --candle-wick-height {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-wick-top {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --candle-x {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-scale {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

@property --line-y {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --line-prev-y {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --line-count {
  syntax: "<integer>";
  inherits: false;
  initial-value: 1;
}

@property --line-scale {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

@layer components {
  @keyframes uiify-otp-input-caret-blink {
    50% {
      opacity: 0;
    }
  }
}

API

Field.Root

الخاصيةالنوعافتراضيالوصف
id*string—معرّف ثابت يمرّره كل جزء كـ fieldId، لربط التسمية وعنصر التحكم والوصف والخطأ في بنية الخادم.
invalidbooleanfalseيعلّم الحقل كغير صالح، ويكشف data-invalid للتنسيق.
requiredbooleanfalseيعلّم الحقل كمطلوب، ويكشف data-required للتنسيق.
disabledbooleanfalseيعلّم الحقل كمعطّل، ويكشف data-disabled للتنسيق.
namestring—مقبول للتوافق مع API؛ ينتمي الاسم إلى عنصر التحكم ولا يُعرض على الغلاف.
renderReactElement | ((props, state) => ReactElement)—استبدل العنصر المعروض مع الحفاظ على توصيلات السلوك وإمكانية الوصول.

Field.Label

الخاصيةالنوعافتراضيالوصف
fieldId*string—المعرّف نفسه المعطى لـ Field.Root، ويُشتق منه htmlFor الذي يشير إلى عنصر التحكم.
disabledbooleanfalseيعلّم التسمية كمعطّلة، ويكشف data-disabled للتنسيق.
requiredbooleanfalseيعلّم التسمية كمطلوبة، ويكشف data-required للتنسيق.

Field.Control

الخاصيةالنوعافتراضيالوصف
asElementType"div"استخدم input أو textarea أو select أو عنصر تحكم مخصّصًا. تُمرّر الخصائص الأصلية ومعالجات الأحداث وstyle وref.
type / name / autoComplete / minLength / maxLength / pattern / readOnlynative control props—توضع خيارات المدخل على Field.Control، ومنها type="password". وتُحفظ دلالات التحقق القياسية وFormData.
fieldId*string—المعرّف نفسه المعطى لـ Field.Root، ويُشتق منه معرّف عنصر التحكم وتوصيلات aria-labelledby وaria-describedby.
disabledbooleanfalseيمرّر disabled الأصلية إلى input وtextarea وselect ويكشف data-disabled.
invalidbooleanfalseيعيّن aria-invalid ويضيف رسالة الخطأ إلى aria-describedby لعنصر التحكم.
requiredbooleanfalseيمرّر required الأصلية إلى input وtextarea وselect، ويعيّن aria-required.
renderReactElement | ((props, state) => ReactElement)—استبدل العنصر المعروض مع الحفاظ على توصيلات السلوك وإمكانية الوصول.

Field.Description

الخاصيةالنوعافتراضيالوصف
fieldId*string—المعرّف نفسه المعطى لـ Field.Root، ويُشتق منه معرّف الوصف الذي يشير إليه عنصر التحكم.
disabledbooleanfalseيعلّم الوصف كمعطّل، ويكشف data-disabled للتنسيق.

Field.Error

الخاصيةالنوعافتراضيالوصف
fieldId*string—المعرّف نفسه المعطى لـ Field.Root، ويُشتق منه معرّف الخطأ الذي يشير إليه عنصر التحكم عند عدم الصلاحية.
invalidbooleanfalseيعرض رسالة الخطأ؛ ولا يعرض الجزء شيئًا ما دامت هذه القيمة false.

لوحة المفاتيح

المفتاحالإجراء
Tabنقل التركيز إلى عنصر التحكم أو منه
Enter / Spaceالتفعيل عند التركيز (لعناصر التحكم الشبيهة بالأزرار)

إمكانية الوصول

يستخدم HTML الدلالي متى أمكن. افحص بنية HTML المعروضة في المختبر؛ لا تُضاف ARIA إلا حين لا تكفي العناصر الأصلية.

نقاط التنسيق

data-disabled
تُرجمت هذه الصفحة آليًا ولم تُراجع بعد.