В веб-разработке иногда возникает потребность визуально повторить содержимое элемента в другом месте страницы. Это похоже на создание «проекции», но не дублируя при этом сам HTML-код.

Сделать это можно на одном CSS без использования JavaScript. CSS отлично справляется со вставкой текста при помощи кастомных data-* в тандеме с псевдоэлементами. Таким образом можно не только дублировать текст, но и создавать всплывающие подсказки (toolkit), украшать заголовки, добавлять любой текст, который не будет влиять на SEO — в том числе отображать перевод на другой язык.

Принцип работы

Ключ к этому методу:

  1. Псевдоэлементы (::before или ::after) позволяют вставить контент до или после содержимого выбранного элемента, не меняя при этом HTML-структуру.
  2. Свойство content: Это свойство позволяет задать содержимое для псевдоэлемента. Самое интересное, что оно может принимать не только строковые значения, но и функцию attr(), которая считывает значение любого атрибута родительского HTML-элемента.

Вы скажете, что CSS-свойство content и так позволяет вставлять строковый текст напрямую (например, content: "Новинка";). Но для кириллических символов это может приводить к проблемам с кодировкой (появлению «кракозябр») в зависимости от настроек сервера, кодировки файла стилей (.css) и его подключения.

Чтобы гарантировать, что ваш текст всегда отображается корректно лучше хранить весь динамический и кириллический текст в пользовательских атрибутах HTML (например, data-copy или data-text), а затем вызывать его через функцию attr():

/* ❌ Ненадежно для кириллицы: */
/* content: "Скидка 50%"; */

/* ✅ Надежно: */
content: attr(data-copy);

Предположим, у нас есть заголовок, и мы хотим создать его полупрозрачную, сдвинутую копию.

Шаг 1: HTML-структура

Для «голограммы» нам понадобится место хранения текста, который хотим дублировать. Используем пользовательский атрибут data-copy.

<h1 class="hologram-title" data-copy="CSS-голограмма">
    CSS-голограмма
</h1>

Шаг 2: базовый CSS и позиционирование

Чтобы переместить псевдоэлемент в нужную точку страницы, нам нужно задать для родительского элемента position: relative, а для самого псевдоэлемента — position: absolute.

.hologram-title {
    font-size: clamp(28px, 6vw, 72px);
    font-family: sans-serif;
    color: #007bff;
    position: relative;
    display: inline-block;
    margin: 50px;
    z-index: 10;
}

Шаг 3: создание и стилизация «голограммы»

Теперь используем ::after для создания проекции.

.hologram-title::after {
    /* Копируем текст из атрибута */
    content: attr(data-copy);
    position: absolute;
    top: 5px;
    left: 10px;
    color: rgba(0, 123, 255, 0.2);
    z-index: 5; /* Отправляем под оригинал */
    pointer-events: none; /* Важно: делаем проекцию некликабельной */
}

Результат

Вот ещё эксперимент: тут я добавила тени для текста (text-shadow), псевдоэлемент ::before и отключила у ::after позиционирование.

Этот пример показывает, как можно использовать сразу два пользовательских атрибута для создания сложного, динамичного, многострочного заголовка с подзаголовками, которые не влияют на SEO, т.к. их нет в DOM, при этом сам HTML-элемент (<h1>) остается однострочным.

Шаг 1: подготовка HTML с двумя атрибутами

В заголовок добавляем два разных атрибута с разным содержимым.

<body class="mode-visible">
    <h1 class="hologram-title" data-copy="без JavaScript ⭐⭐⭐⭐⭐⭐⭐" data-twocopy="Псевдоэлементы">
        Текст-призрак
    </h1>
</body>

Шаг 2: создание многослойного эффекта

Визуально это разбивается на 4 строки:

  1. ::before (текст с эмодзи из data-copy)
  2. <h1> (основной текст)
  3. ::after (текст из data-twocopy)
  4. В этом примере получается три основных текстовых элемента, но благодаря большим line-height в ::before и смещениям, эмодзи занимает место четвёртой строки.

А. Основные стили

Настраиваем фон и основной заголовок. Использование position: relative для правильного позиционирования псевдоэлементов, указываем цвет основного заголовка.

body {
    background: radial-gradient(circle at center, #2a2340, #1a142b);
    padding: 20px;
}

.hologram-title {
    font-size: 72px;
    font-family: "Arial Black", sans-serif;
    font-style: italic;
    position: relative;
    display: inline-block;
    padding: 20px 30px;
    margin: 80px 0 30px 80px;
    text-transform: uppercase;
    letter-spacing: -4px;
    color: #e0d4ff; /* Основной цвет текста */
}

B. Верхняя строка с эмодзи

Псевдоэлемент ::before располагается выше, добавила туда эмодзи звёздочки, увеличу line-height, чтобы они оказались в самом низу.

.hologram-title::before {
    content: attr(data-copy);
    position: absolute;
    top: -82px;
    left: -20px;
    color: #ff00c8;
    opacity: 0.5;
    filter: blur(2px);
    font-style: normal; /* Сброс курсива, чтобы отличаться от <h1> */
    pointer-events: none;
    line-height: 2.5; /* Занимает много вертикального пространства */
    animation: pulse 2.5s infinite ease-in-out;
    z-index: -1;
}

C. Нижняя строка с искажением

Псевдоэлемент ::after располагается ниже и имеет совершенно другой шрифт, цвет и небольшой наклон.

.hologram-title::after {
    content: attr(data-twocopy);
    position: absolute;
    top: 80px; /* Смещение вниз */
    left: -20px;
    color: #00ff16;
    opacity: 0.25;
    font-size: 60px;
    transform: skew(-10deg) rotate(-2deg);
    pointer-events: none;
    font-family: "Papyrus", fantasy; /* Другой шрифт */
    letter-spacing: 6px;
    animation: floaty 4s infinite ease-in-out;
    z-index: -1;
}

Шаг 3: Динамические анимации

Добавляем лёгкие анимации, чтобы оживить эффект и придать ему небольшой киберпанковский вид:

/* --- АНИМАЦИИ --- */
/* Мерцание верхней проекции */
@keyframes pulse {
    0%, 100% { opacity: 0.5; transform: scale(1); }
    50% { opacity: 0.8; transform: scale(1.01); }
}

/* "Плавающее" искажение нижней проекции */
@keyframes floaty {
    0%, 100% { transform: skew(-10deg) rotate(-2deg) translateY(0); }
    50% { transform: skew(-10deg) rotate(-2deg) translateY(-5px); }
}

Результат

В этом примере используется пользовательский атрибут data-translate, чтобы создать строки с переводом и всплывающую подсказку.

Алгоритм действий остаётся таким же, как и в предыдущих примерах. Здесь атрибуты добавлены к заголовку и окну приветствия. В параграфах (<p>) используется тот же принцип: псевдоэлементы (::after) отображают полный перевод всего абзаца, прикрепляя его к концу блока — без абсолютного позиционирования. Это удобно для учебных материалов: оригинал виден всегда, а перевод напоминает подстрочник. При желании можно настроить отображение перевода по наведению.

Результат

Как сделать всплывающую подсказку для слов

Для отдельных слов, используется HTML-тег с классом .tooltip. Всплывающий текст хранится в его атрибуте:

<span class="tooltip" data-translate="Striking example">яркий пример</span>

Вариант на одном CSS

Подсказка всплывает по наведению (hover):

.tooltip {
  position: relative;
  cursor: pointer;
  color: #007bff;
  text-decoration: underline dotted;
}

.tooltip::after {
  content: attr(data-copy);
  position: absolute;
  bottom: 120%;
  left: 0;
  background: #007bff;
  color: #fff;
  padding: 6px 10px;
  border-radius: 8px;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.3s ease, transform 0.3s ease;
  pointer-events: none;
  z-index: 10;
  box-shadow:0 0 10px #6f42c1
}

.tooltip:hover::after {
  opacity: 1;
  transform: translateY(0);
}

Вариант с JavaScript

После клика по слову над ним появится всплывающая подсказка.

.tooltip {
    cursor: pointer;
    color: #007bff;
    text-decoration: underline dotted;
}
.tooltip-popup {
    position: absolute;
    background: #007bff;
    color: #fff;
    padding: 6px 10px;
    border-radius: 8px;
    white-space: nowrap;
    z-index: 10;
    box-shadow: 0 0 10px #6f42c1;
}
document.addEventListener('click', (e) => {
    const target = e.target;
    if (target.classList.contains('tooltip')) {
        const existing = document.querySelector('.tooltip-popup');
        if (existing) existing.remove();

        const popup = document.createElement('div');
        popup.className = 'tooltip-popup';
        popup.textContent = target.dataset.translate;

        document.body.appendChild(popup);

        const rect = target.getBoundingClientRect();
        popup.style.top = `${rect.top + window.scrollY - 35}px`;
        popup.style.left = `${rect.left + window.scrollX}px`;

        setTimeout(() => popup.remove(), 3000);
    }
});

Где ещё можно использовать

Этот метод подходит для создания только декоративных, неинтерактивных эффектов, без влияния на SEO.

  • Например, можно отобразить в углу или поверх блока текст: «Beta», «Demo», «Preview».
  • На карточке товара: data-status= «Новинка», «Скидка», «Распродажа».
  • Как ambient-слой в дизайне.
  • В админке или предпросмотре: «Только для модератора», «Черновик».
ХарактеристикаОписание
Только текстНельзя скопировать сложную структуру (теги <img>, <a>, <span> и т.д.). Псевдоэлемент может отобразить только текст или изображение через URL.
Не интерактивноПроекция, созданная ::before или ::after, некликабельна. Она не является частью DOM. Рекомендуется использовать pointer-events: none;.
Дублирование данныхЧтобы скопировать текст, его приходится дублировать (в атрибуте data-copy).

Текст внутри тега и текст в data-* — это два разных места. И если вы хотите, чтобы они всегда были одинаковыми, придётся вручную следить за синхронизацией.

Например, в WordPress можно автоматизировать это через PHP:

<h1 class="hologram-title" data-copy="<?php echo $title; ?>">
  <?php echo $title; ?>
</h1>

Оставьте комментарий

Комментарии, считающиеся спамом или исключительно рекламные по своему содержанию, будут удалены. Допускается включение ссылки на соответствующий контент, но текст должен иметь отношение к теме поста. Также будут удалены анонимные, оскорбительные, содержащие ненормативную лексику, вводящие в заблуждение или клеветнические комментарии.

Для публикации кода в комментариях используйте следующие шорткоды -
[php]ваш код[/php] - для PHP, [css]ваш код[/css] - для CSS, [code lang="js"]ваш код[/code] - для HTML или JS, указав соответствующий lang.

Ваш адрес email не будет опубликован. Обязательные поля помечены *