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

Stroke Fill Scroll

Контурный заголовок заливается цветом по мере прохождения через кадр — индикатор прогресса, живущий прямо в буквах, ноль скриптов. Копия текста с градиентом, вырезанным по глифам (background-clip: text), растёт через background-size на animation-timeline: view() — и отматывается назад вместе с прокруткой.

cssscrollrevealintermediatereduced-motion ✓
↗
↕ демо длиннее кадра — прокрутите его
🎛 Настройки — крути и копируй

Пропсы

Разобрано из кода записи: обязательные — без «?», у остальных показано значение по умолчанию.

ПропТипПо умолчаниюЧто это
text?string"Прогресс живёт в буквах"Текст заголовка.
strokeWidth?number1.2Толщина контура, px (формат «1.2»).
fontSize?string"clamp(2rem, 7cqw, 3.6rem)"Кегль: любое CSS-значение font-size.

Установка

npx shadcn@latest add https://motiva.pages.dev/r/stroke-fill-scroll.json
Что нужно в проекте до установки
  1. Файл components.json в корне

    Из него CLI узнаёт, куда класть файлы. Алиасы — в такой форме:

    "aliases": { "components": "@/components", "lib": "@/lib" }

    Нет файла — установка не начнётся вовсе.

  2. Алиас @ объявлен дважды

    В корневом tsconfig.json и в конфиге сборщика. В Vite корневой tsconfig часто содержит только references — этого мало.

    "paths": { "@/*": ["./src/*"] }        // tsconfig.json
    resolve.alias = { "@": "/src" }        // vite.config.ts

    Без paths CLI молча создаёт папку с именем @.

  3. Токены подключены один раз на приложение

    Они приезжают вместе с записью — достаточно импортировать их в точке входа:

    import "@/styles/motiva-tokens.css";

    Без них блок потеряет цвета, ритм и тему.

  4. Своя типографика не спорит с блоком

    Наследуемые свойства протекают внутрь: text-align: center на обёртке отцентрует содержимое секции. То же касается font-familyи line-height.

  5. Проект без TypeScript? всё равно нужен tsconfig

    CLI грузит tsconfig.json безусловно, а при "tsx": false идёт за jsconfig.json и падает так же. Минимум — файл с pathsи "tsx": true в components.json.

    Приехавший рядом .tsx можно не трогать — вам нужен .vanilla.js.

Экспорт

↓ Скачать .zip

Код

.sfsc {
  position: relative;
  margin: 0;
  color: transparent;
  -webkit-text-stroke: var(--sfsc-stroke) var(--ds-muted, oklch(0.52 0.02 265));
  /* правка width/height не влияет на заливку: она идёт background-size у копии */
}

/* заливка — КОПИЯ текста поверх контура: градиент акцента, вырезанный по глифам.
   База = залито целиком: без поддержки прокруточных анимаций, под reduce и data-shot
   заголовок просто цветной */
.sfsc::before {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  color: transparent;
  -webkit-text-stroke: 0;
  background-image: linear-gradient(
    90deg,
    var(--ds-accent, oklch(0.72 0.16 300)),
    oklch(from var(--ds-accent, oklch(0.72 0.16 300)) calc(l + 0.12) c calc(h + 30))
  );
  background-repeat: no-repeat;
  background-size: 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
}

@supports (animation-timeline: view()) {
  /* начальное состояние ВНУТРИ @supports: там, где прокруточных анимаций нет,
     заголовок обязан остаться залитым, а не пустым навсегда */
  .sfsc::before {
    background-size: 0% 100%;
    animation: sfsc-fill linear both;
    animation-timeline: view();
    /* субъект мал, contain применим; заливка заканчивается к середине кадра —
       читатель видит её завершённой, а не догоняет у кромки */
    animation-range: contain 0% cover 55%;
  }
  @keyframes sfsc-fill {
    to { background-size: 100% 100%; }
  }
}

@media (prefers-reduced-motion: reduce) { .sfsc::before { animation: none; } }
html[data-shot] .sfsc::before { animation: none; }
лицензия: MITавтор: Motivaисточник: оригинал (Motiva); идея-референс — aristidebenoist.com (заливка stroke-текста прокруткой)v1.0.0