Motiva
Мои коллекции
← каталог

Skeleton Shimmer

Скелетон-карточка с бегущим бликом: одна keyframes-анимация синхронно ведёт shimmer по всем «костям».

cssautobasic0reduced-motion ✓

Установка

npx shadcn@latest add https://motiva.pages.dev/r/skeleton-shimmer.json
Что нужно в проекте до установки
  1. components.json в корне — с алиасами в форме"components": "@/components". Нет файла — CLI не поймёт, куда класть.
  2. Алиас @ объявлен дважды: "paths": {"@/*": ["./src/*"]} в корневом tsconfig.json (в Vite он часто содержит только references — тогда CLI создаёт папку@) и resolve.alias в конфиге сборщика.
  3. Токены подключены один раз на приложение:import "@/styles/motiva-tokens.css" — они приезжают вместе с записью. Без них секция потеряет цвета, ритм и тему.
  4. Своя типографика не должна спорить: если у вас на обёрткеtext-align: center, он протечёт внутрь блоков.
  5. Проект без TypeScript? Всё равно нужен минимальныйtsconfig.json с pathsи "tsx": true в components.json: CLI грузит tsconfig безусловно, а с "tsx": false идёт за jsconfig.json и падает так же. Приехавший рядом.tsx можно не трогать — вам нужен.vanilla.js.

Экспорт

↓ Скачать .zip

Код

import type { CSSProperties } from "react";

const bone = (extra: CSSProperties): CSSProperties => ({
  borderRadius: 8,
  background: "oklch(0.24 0.015 270)",
  position: "relative",
  overflow: "hidden",
  ...extra,
});

/**
 * Скелетон-карточка с бегущим бликом (shimmer): градиентная полоса едет по «костям»
 * синхронно (общая keyframes-анимация). Чистый CSS. Reduced-motion/data-shot — блик статичен.
 */
export function SkeletonShimmer() {
  return (
    <div
      className="sk-card"
      style={{
        width: "min(320px, 84vw)",
        padding: "1.2rem",
        borderRadius: 16,
        border: "1px solid oklch(0.3 0.02 270)",
        background: "oklch(0.18 0.02 270)",
        display: "grid",
        gap: 12,
      }}
      aria-busy="true"
      aria-label="Загрузка контента"
    >
      <style>{`
        .sk-card [data-bone]::after {
          content: "";
          position: absolute;
          inset: 0;
          transform: translateX(-100%);
          background: linear-gradient(100deg, transparent 30%, oklch(0.5 0.02 270 / 0.35) 50%, transparent 70%);
          animation: sk-sweep 1.6s ease-in-out infinite;
        }
        @keyframes sk-sweep { to { transform: translateX(100%); } }
        @media (prefers-reduced-motion: reduce) {
          .sk-card [data-bone]::after { animation: none; }
        }
      `}</style>
      <div style={{ display: "flex", gap: 12, alignItems: "center" }}>
        <div data-bone style={bone({ width: 44, height: 44, borderRadius: "50%" })} />
        <div style={{ display: "grid", gap: 8, flex: 1 }}>
          <div data-bone style={bone({ height: 12, width: "70%" })} />
          <div data-bone style={bone({ height: 10, width: "45%" })} />
        </div>
      </div>
      <div data-bone style={bone({ height: 130, borderRadius: 12 })} />
      <div style={{ display: "grid", gap: 8 }}>
        <div data-bone style={bone({ height: 10 })} />
        <div data-bone style={bone({ height: 10, width: "85%" })} />
        <div data-bone style={bone({ height: 10, width: "60%" })} />
      </div>
    </div>
  );
}
лицензия: MITавтор: Motivaисточник: оригинал (Motiva)v1.0.0