feat: typewriter-эффект в логотипе dedinit() — темы блога в скобках
- layouts/partials/extend_footer.html: инлайн-скрипт (печать/пауза/стирание/мигание, prefers-reduced-motion, слова из [params.typewriter]) - assets/css/extended/typewriter.css: блочный мигающий курсор - hugo.toml: [params.typewriter] words - openspec/changes/header-typewriter: proposal/design/specs/tasks Без зависимостей (Alpine не нужен), тема не модифицируется.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# Design: Анимированный логотип-печатная машинка (header typewriter)
|
||||
|
||||
## Context
|
||||
|
||||
- Тема: rDedInit (форк PaperMod), Hugo Extended 0.165.x.
|
||||
- Логотип в шапке — партиал `themes/rDedInit/layouts/partials/header.html`,
|
||||
ссылка `<a class="logo">` содержит текст `{{ $label_text }}` = `dedinit()`
|
||||
(`site.Params.label.text`).
|
||||
- Хуки темы: `layouts/partials/extend_head.html` (в `<head>`) и
|
||||
`layouts/partials/extend_footer.html` (перед `</body>`). Локальные layouts/
|
||||
переопределяют тему. В `footer.html` темы есть
|
||||
`{{- partial "extend_footer.html" . }}` — подтверждено grep.
|
||||
- CSS: тема собирает всё из `assets/css/extended/*.css` в один
|
||||
`extended.css` (в `head.html`: `resources.Match "css/extended/*.css"`).
|
||||
|
||||
## Files
|
||||
|
||||
1. `layouts/partials/extend_footer.html` (НОВЫЙ) — подключает inline `<script>`.
|
||||
Hugo сам добавит партиал в футер всех страниц (переопределение темы).
|
||||
Скрипт маленький (~1 КБ), inline — без отдельного assets/js (меньше
|
||||
запросов; для трёхстрочного эффекта отдельный бандл избыточен).
|
||||
Слова передаём из Hugo: `data-words="{{ delimit (site.Params.typewriter.words | default (slice "linux" "llm" "politics" "homelab" "nix")) "," }}"`.
|
||||
|
||||
2. `assets/css/extended/typewriter.css` (НОВЫЙ) — курсор и вставка:
|
||||
```css
|
||||
#dedinit-typewriter { display:inline-block; }
|
||||
#dedinit-typewriter .tw-cursor { ... блочный квадратик, animation: tw-blink 1s steps(1) infinite ... }
|
||||
@keyframes tw-blink { 50% { opacity: 0; } }
|
||||
@media (prefers-reduced-motion: reduce) { #dedinit-typewriter .tw-cursor { animation: none; } }
|
||||
```
|
||||
Курсор — всегда видимый блок `currentColor`, чтобы не зависеть от схемы
|
||||
(light/dark).
|
||||
|
||||
3. `hugo.toml` (ОПЦИОНАЛЬНО) — `[params.typewriter] words = [...]`. Если не
|
||||
задан — скрипт использует фолбэк. Не обязателен для эффекта.
|
||||
|
||||
## JS logic (inline in extend_footer.html)
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const a = document.querySelector('header a.logo');
|
||||
if (!a || a.dataset.twDone) return;
|
||||
const words = (a.dataset.words || 'linux,llm,politics,homelab,nix').split(',');
|
||||
// вставить <span id="dedinit-typewriter"><span class="tw-cursor"></span></span>
|
||||
// перед ')' в тексте логотипа; первый клик -- печать
|
||||
// тайминги: type 90ms/char, hold 1400ms, erase 50ms/char, gap 400ms
|
||||
if (matchMedia('(prefers-reduced-motion: reduce)').matches) { первый word статично }
|
||||
})();
|
||||
```
|
||||
|
||||
- Символы печатаются через `textContent` — безопасно (нет HTML-инъекции из
|
||||
слов; слова приходят из конфига автора).
|
||||
- Слово не должно разрываться: `white-space: nowrap` на тизере.
|
||||
- Курсор мигает на пустом месте между циклами (это и есть «моргает на месте»).
|
||||
|
||||
## Apply
|
||||
|
||||
```bash
|
||||
# 1. создать файлы (выше), 2. собрать:
|
||||
make build # hugo --minify
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
grep -n "dedinit-typewriter" public/index.html # скрипт + span присутствуют
|
||||
grep -n "tw-cursor" public/index.html # CSS-курсор
|
||||
grep -n "prefers-reduced-motion" public/**.html # a11y-заглушка
|
||||
# в браузере: логотип печатает/стирает слова по циклу; курсор мигает;
|
||||
# с prefers-reduced-motion — первое слово статично
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
```bash
|
||||
git rm layouts/partials/extend_footer.html assets/css/extended/typewriter.css
|
||||
# (и убрать [params.typewriter] из hugo.toml, если добавлялся)
|
||||
make build
|
||||
```
|
||||
|
||||
Публикация/деплой — по согласованию с пользователем (`make deploy`).
|
||||
@@ -0,0 +1,42 @@
|
||||
# Proposal: Анимированный логотип-печатная машинка для dedinit()
|
||||
|
||||
## Why
|
||||
|
||||
Логотип в шапке сайта `dedinit()` — статический текст. Хочется «оживить»
|
||||
брендинг: внутри круглых скобок в логотипе должен появляться блочный курсор и
|
||||
«печатать» по одному символу темы/теги блога (linux, llm, politics, homelab...),
|
||||
замирать на секунду, стирать слово посимвольно, мигать на пустом месте и снова
|
||||
печатать следующее слово. Это декоративный эффект печатной машинки
|
||||
(typewriter), который обыгрывает название блога как вызов функции
|
||||
`dedinit(...)` — «инициализация» блога.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Новый крошечный JS-инлайн (~1 КБ, без зависимостей, без Alpine.js):
|
||||
- Находит тег `<a class="logo">` на странице, внутри текста `dedinit()`
|
||||
выделяет скобки, вставляет `<span id="dedinit-typewriter">` между `( )`.
|
||||
- Цикл: печать слова по 1 символу (быстро), пауза (~1.4 с), стирание по
|
||||
1 символу, пауза с курсором (~0.4 с), следующее слово.
|
||||
- Слова берутся из `data-words` атрибута (передаются из Hugo конфига,
|
||||
`site.Params.typewriter.words`), фолбэк на массив по умолчанию.
|
||||
- Уважает `prefers-reduced-motion`: при включённом — вместо анимации
|
||||
показывается первое слово статически.
|
||||
- Если JS выключен — логотип остаётся обычным `dedinit()` (прогрессивное
|
||||
улучшение).
|
||||
- Новый CSS в `assets/css/extended/typewriter.css` (тема PaperMod
|
||||
автоматически подхватывает всё из `assets/css/extended/`):
|
||||
- Блочный курсор в виде квадратика с миганием (CSS-анимация, не JS).
|
||||
- Стили для слова-тизера в скобках.
|
||||
- Хук JS через переопределение партиала `layouts/partials/extend_footer.html`
|
||||
(Hugo: локальные layouts переопределяют тему; там подключаем `<script>`).
|
||||
|
||||
## Impact
|
||||
|
||||
- Затронутые файлы:
|
||||
- `layouts/partials/extend_footer.html` (новый, переопределяет тему)
|
||||
- `assets/css/extended/typewriter.css` (новый)
|
||||
- `hugo.toml` — необязательно: `[params.typewriter] words = [...]` (опция)
|
||||
- Тема `themes/rDedInit` НЕ модифицируется (правки не затрутся апдейтом темы).
|
||||
- Контент постов, RSS, разметка списков — не меняются.
|
||||
- Откат (rollback): удалить 2 новых файла (и, если добавляли, параметр
|
||||
`typewriter` в hugo.toml), пересобрать — сайт вернётся к статическому логотипу.
|
||||
@@ -0,0 +1,86 @@
|
||||
# Site Header Typewriter Capability
|
||||
|
||||
## Purpose
|
||||
|
||||
Логотип `dedinit()` в шапке сайта анимирован эффектом печатной машинки:
|
||||
внутри скобок блочный курсор печатает темы блога, замирает, стирает их и
|
||||
переходит к следующей — обыгрывая название как вызов функции `dedinit(...)`.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Анимация «печатная машинка» в логотипе
|
||||
|
||||
Система SHALL анимировать текст в скобках логотипа `dedinit()` на всех
|
||||
страницах сайта: блочный курсор печатает слово по одному символу, удерживает
|
||||
его ~1 секунду, стирает по одному символу, мигает на пустом месте и печатает
|
||||
следующее слово — бесконечным циклом по списку тем блога.
|
||||
|
||||
#### Scenario: Логотип печатает слова по циклу
|
||||
|
||||
- **GIVEN** открыта любая страница сайта (`/` или внутренняя), JS включён
|
||||
- **WHEN** страница загружена
|
||||
- **THEN** в `dedinit()` между скобками присутствует контейнер
|
||||
`#dedinit-typewriter` с блочным мигающим курсором, и текст посимвольно
|
||||
печатается из списка тем (например, `linux` → `llm` → `politics` → ...)
|
||||
|
||||
#### Scenario: Цикл без остановки
|
||||
|
||||
- **GIVEN** анимация запущена
|
||||
- **WHEN** прошло более 3 слов печати/стирания
|
||||
- **THEN** анимация продолжает цикл (после последнего слова переходит к
|
||||
первому), не останавливаясь и не накапливая ошибок
|
||||
|
||||
### Requirement: Бесшовный вывод (прогрессивное улучшение)
|
||||
|
||||
Система SHALL оставаться декоративным улучшением: при выключенном JS логотип
|
||||
остаётся обычным текстом `dedinit()`; при его включении — анимация
|
||||
накладывается, не ломая вёрстку и навигацию шапки.
|
||||
|
||||
#### Scenario: JS отключён — логотип статичен
|
||||
|
||||
- **GIVEN** браузер с отключённым JavaScript
|
||||
- **WHEN** страница загружена
|
||||
- **THEN** в шапке отображается обычный текст `dedinit()` без скобочного
|
||||
контейнера и без ошибок
|
||||
|
||||
### Requirement: Доступность (prefers-reduced-motion)
|
||||
|
||||
Система SHALL уважать системную настройку `prefers-reduced-motion: reduce`:
|
||||
при ней показывать первое слово списка статически, без анимации и мигания.
|
||||
|
||||
#### Scenario: Уменьшение движения
|
||||
|
||||
- **GIVEN** у пользователя включено `prefers-reduced-motion: reduce`
|
||||
- **WHEN** страница загружена
|
||||
- **THEN** внутри скобок логотипа отображается первое слово списка статично,
|
||||
курсор не мигает (CSS `animation: none`), JS-цикл не запускается
|
||||
|
||||
### Requirement: Минимальный размер и отсутствие зависимостей
|
||||
|
||||
Система SHALL реализовывать эффект без внешних зависимостей (без Alpine.js,
|
||||
без jQuery): инлайн-скрипт и CSS суммарно не более ~2 КБ.
|
||||
|
||||
#### Scenario: Нет внешних библиотек
|
||||
|
||||
- **GIVEN** собранный сайт
|
||||
- **WHEN** просматривается HTML страницы
|
||||
- **THEN** в `<head>`/футере не подключено никаких сторонних JS-библиотек для
|
||||
эффекта (только собственный инлайн-скрипт и CSS темы)
|
||||
|
||||
### Requirement: Список тем конфигурируется
|
||||
|
||||
Система SHALL позволять задавать список тем через `hugo.toml`
|
||||
(`[params.typewriter] words = [...]`), со встроенным фолбэком по умолчанию
|
||||
(`linux, llm, politics, homelab, nix`) при его отсутствии.
|
||||
|
||||
#### Scenario: Слова из конфига
|
||||
|
||||
- **GIVEN** в `hugo.toml` задан `[params.typewriter] words = ["linux","llm","politics"]`
|
||||
- **WHEN** страница собрана и открыта
|
||||
- **THEN** анимация печатает именно эти слова, в заданном порядке
|
||||
|
||||
#### Scenario: Конфиг не задан
|
||||
|
||||
- **GIVEN** в `hugo.toml` нет `[params.typewriter]`
|
||||
- **WHEN** страница собрана и открыта
|
||||
- **THEN** используется фолбэк-список по умолчанию, анимация работает
|
||||
@@ -0,0 +1,37 @@
|
||||
# Tasks: Header Typewriter
|
||||
|
||||
## 1. Реализация
|
||||
|
||||
- [x] 1.1 Создать `assets/css/extended/typewriter.css` — стили контейнера
|
||||
`#dedinit-typewriter` и блочного курсора `.tw-cursor` (мигание через
|
||||
`@keyframes`, `prefers-reduced-motion` отключает анимацию)
|
||||
- [x] 1.2 Создать `layouts/partials/extend_footer.html` — инлайн-скрипт цикла
|
||||
печати/стирания слов (тайминги: type ~90ms, hold ~1400ms, erase ~50ms,
|
||||
gap ~400ms), вставка контейнера в логотип `dedinit()`, обработка
|
||||
`prefers-reduced-motion`, фолбэк-список слов
|
||||
- [x] 1.3 Проверить, что `extend_footer.html` реально вызывается темой:
|
||||
`grep -n "extend_footer" themes/rDedInit/layouts/partials/footer.html`
|
||||
|
||||
## 2. Сборка и проверка
|
||||
|
||||
- [x] 2.1 Собрать сайт: `make build` — без ошибок (exit 0)
|
||||
- [x] 2.2 Проверить в `public/index.html` наличие `dedinit-typewriter`
|
||||
(`grep -c "dedinit-typewriter" public/index.html` >= 1)
|
||||
- [x] 2.3 Проверить наличие `.tw-cursor` и `prefers-reduced-motion` в собранном CSS
|
||||
(`grep -c "tw-cursor" public/**/*.css` >= 1)
|
||||
- [x] 2.4 Логика цикла проверена эмуляцией (node): печать до полного слова,
|
||||
пауза, стирание до пустоты, пауза-мигание, переход к следующему;
|
||||
после последнего слова цикл замыкается; все слова проходят полный цикл
|
||||
(см. /tmp/tw_test.js)
|
||||
|
||||
## 3. Git
|
||||
|
||||
- [ ] 3.1 Закоммитить изменения (layouts/, assets/, openspec/, при желании
|
||||
hugo.toml с [params.typewriter]) и запушить в gitverse (после
|
||||
согласования с пользователем)
|
||||
- [ ] 3.2 Обновить `docs/STATUS.md` / `docs/TODO.md` при необходимости
|
||||
|
||||
## 4. Деплой (по согласованию)
|
||||
|
||||
- [ ] 4.1 `make deploy` (rsync public/ → kpa39l.myjino.ru:2222)
|
||||
- [ ] 4.2 Проверить на https://dedinit.ru/ — логотип печатает темы
|
||||
Reference in New Issue
Block a user