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

Spinner

مؤشر متحرك لحالة التحميل.

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

المختبر

Spinner

Sizes

Label

Loading content
Loading content

Value

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

export default function Example() {
  return <Spinner className="" label="Loading results" />;
}
@property --uiify-spinner-tail {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

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

@layer components {
  [data-uiify-spinner] {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--primary);
  }

  [data-uiify-spinner] > [data-part="label"] {
    color: var(--foreground);
  }

  [data-uiify-spinner]::before {
    flex: none;
    display: block;
    inline-size: 2rem;
    block-size: 2rem;
    content: "";
    border-radius: 50%;
    background: conic-gradient(
      from calc(-90deg + var(--uiify-spinner-tail)),
      currentcolor 0 var(--uiify-spinner-length),
      transparent 0
    );
    mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 3px));
    animation:
      uiify-spinner-rotate 2s linear infinite,
      uiify-spinner-orbit 1.5s ease-in-out infinite;
  }

  [data-uiify-spinner][data-value]::before {
    --uiify-spinner-tail: 0deg;
    --uiify-spinner-length: calc(attr(data-value type(<number>), 0) * 3.6deg);

    background: conic-gradient(
      from 0deg,
      currentcolor 0 var(--uiify-spinner-length),
      color-mix(in oklch, currentcolor 20%, transparent) 0
    );
    animation: none;
  }

  [data-uiify-spinner][data-size="sm"]::before {
    inline-size: 1.25rem;
    block-size: 1.25rem;
    mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 1px));
  }

  [data-uiify-spinner][data-size="lg"]::before {
    inline-size: 6.25rem;
    block-size: 6.25rem;
    mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 3px));
  }

  [data-uiify-spinner][data-align="center"] {
    margin-inline: auto;
  }

  [data-uiify-spinner][data-align="fixed"],
  [data-uiify-spinner][data-align="absolute"] {
    inset-block-start: 50%;
    inset-inline-start: 50%;
    translate: -50% -50%;
  }

  [data-uiify-spinner][data-align="fixed"] {
    position: fixed;
  }

  [data-uiify-spinner][data-align="absolute"] {
    position: absolute;
  }

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

  @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;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    [data-uiify-spinner]:not([data-value])::before {
      animation: uiify-spinner-orbit 2.5s ease-in-out infinite;
    }
  }
}
import { Spinner } from "@gunkin/uiify";

export default function Example() {
  return (
    <Spinner
      className={
        "relative inline-flex items-center [gap:0.5rem] text-primary [&>[data-part=label]]:text-foreground before:fl" +
        "ex-none before:block before:[inline-size:2rem] before:[block-size:2rem] before:content-[''] before:rounded-" +
        "[50%] before:[background:conic-gradient(_from_calc(-90deg_+_var(--uiify-spinner-tail)),_currentcolor_0_var(" +
        "--uiify-spinner-length),_transparent_0_)] before:[mask:radial-gradient(farthest-side,_transparent_calc(100%" +
        "_-_4px),_#000_calc(100%_-_3px))] before:[animation:uiify-spinner-rotate_2s_linear_infinite,_uiify-spinner-o" +
        "rbit_1.5s_ease-in-out_infinite] data-[value]:before:[--uiify-spinner-tail:0deg] data-[value]:before:[--uiif" +
        "y-spinner-length:calc(attr(data-value_type(<number>),_0)_*_3.6deg)] data-[value]:before:[background:conic-g" +
        "radient(_from_0deg,_currentcolor_0_var(--uiify-spinner-length),_color-mix(in_oklch,_currentcolor_20%,_trans" +
        "parent)_0_)] data-[value]:before:[animation:none] data-[size=sm]:before:[inline-size:1.25rem] data-[size=sm" +
        "]:before:[block-size:1.25rem] data-[size=sm]:before:[mask:radial-gradient(farthest-side,_transparent_calc(1" +
        "00%_-_3px),_#000_calc(100%_-_1px))] data-[size=lg]:before:[inline-size:6.25rem] data-[size=lg]:before:[bloc" +
        "k-size:6.25rem] data-[size=lg]:before:[mask:radial-gradient(farthest-side,_transparent_calc(100%_-_4px),_#0" +
        "00_calc(100%_-_3px))] data-[align=center]:[margin-inline:auto] data-[align=fixed]:[inset-block-start:50%] d" +
        "ata-[align=fixed]:[inset-inline-start:50%] data-[align=fixed]:[translate:-50%_-50%] data-[align=absolute]:[" +
        "inset-block-start:50%] data-[align=absolute]:[inset-inline-start:50%] data-[align=absolute]:[translate:-50%" +
        "_-50%] data-[align=fixed]:fixed data-[align=absolute]:absolute motion-reduce:[&:not([data-value])::before]:" +
        "[animation:uiify-spinner-orbit_2.5s_ease-in-out_infinite]"
      }
      label="Loading results"
    />
  );
}
/* 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('relative inline-flex items-center [gap:0.5rem] text-primary [&>[data-part=label]]:text-foreground before:flex-none before:block before:[inline-size:2rem] before:[block-size:2rem] before:rounded-[50%] before:[background:conic-gradient(_from_calc(-90deg_+_var(--uiify-spinner-tail)),_currentcolor_0_var(--uiify-spinner-length),_transparent_0_)] before:[mask:radial-gradient(farthest-side,_transparent_calc(100%_-_4px),_#000_calc(100%_-_3px))] before:[animation:uiify-spinner-rotate_2s_linear_infinite,_uiify-spinner-orbit_1.5s_ease-in-out_infinite] data-[value]:before:[--uiify-spinner-tail:0deg] data-[value]:before:[--uiify-spinner-length:calc(attr(data-value_type(<number>),_0)_*_3.6deg)] data-[value]:before:[background:conic-gradient(_from_0deg,_currentcolor_0_var(--uiify-spinner-length),_color-mix(in_oklch,_currentcolor_20%,_transparent)_0_)] data-[value]:before:[animation:none] data-[size=sm]:before:[inline-size:1.25rem] data-[size=sm]:before:[block-size:1.25rem] data-[size=sm]:before:[mask:radial-gradient(farthest-side,_transparent_calc(100%_-_3px),_#000_calc(100%_-_1px))] data-[size=lg]:before:[inline-size:6.25rem] data-[size=lg]:before:[block-size:6.25rem] data-[size=lg]:before:[mask:radial-gradient(farthest-side,_transparent_calc(100%_-_4px),_#000_calc(100%_-_3px))] data-[align=center]:[margin-inline:auto] data-[align=fixed]:[inset-block-start:50%] data-[align=fixed]:[inset-inline-start:50%] data-[align=fixed]:[translate:-50%_-50%] data-[align=absolute]:[inset-block-start:50%] data-[align=absolute]:[inset-inline-start:50%] data-[align=absolute]:[translate:-50%_-50%] data-[align=fixed]:fixed data-[align=absolute]:absolute motion-reduce:[&:not([data-value])::before]:[animation:uiify-spinner-orbit_2.5s_ease-in-out_infinite]');

@source inline("before:content-['']");

@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

Spinner

الخاصيةالنوعافتراضيالوصف
asElementType"div"يغيّر وسم الجذر المعروض، مثل as="span".
classNamestring—تُمرّر أسماء الفئات إلى العنصر الجذر للتنسيق على العميل.
align"center" | "fixed" | "absolute"—يضع مؤشر الانتظار في التدفق العادي، أو يثبّته إلى إطار العرض، أو يموضعه تموضعًا مطلقًا.
childrenReactNode—محتوى مرئي يُعرض مع اسم منطقة الحالة للتقنيات المساعدة.
valuenumber—نسبة مئوية ثابتة من 0 إلى 100. تستبدل الحركة بقوس ثابت وتحوّل الدور إلى progressbar.
delaynumber0مهلة بالمللي ثانية قبل ظهور مؤشر الانتظار، وتُطبّق في CSS فلا تحتاج إلى JavaScript.
labelstring"Loading"اسم تعلنه منطقة الحالة للتقنيات المساعدة.
showLabelbooleanfalseيعرض التسمية المرئية كجزء [data-part="label"].
size"small" | "default" | "large""default"يتحكّم في قطر مؤشر الانتظار المعروض بـ CSS وحدها.
renderReactElement | ((props, state) => ReactElement)—استبدل العنصر المعروض مع الحفاظ على توصيلات السلوك وإمكانية الوصول.

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

المفتاحالإجراء

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

يستخدم منطقة حية ذات role="status" مع aria-label. يعتمد المؤشر البصري على CSS وحدها ولا يعرض SVG.

نقاط التنسيق

data-uiify-spinnerdata-statedata-delaydata-valuedata-sizedata-aligndata-part="label"--uiify-spinner-tail--uiify-spinner-length
تُرجمت هذه الصفحة آليًا ولم تُراجع بعد.