diff --git a/docs/ANIMATIONS.md b/docs/ANIMATIONS.md index a9c755c..8189fb5 100644 --- a/docs/ANIMATIONS.md +++ b/docs/ANIMATIONS.md @@ -1,3 +1,10 @@ +--- +title: Legal notice +permalink: /docs/animation/index.html +layout: page +tags: ['docs'] +--- + # Hand-Drawn Animation Effects This project includes a sustainable animation system for adding hand-drawn, organic animation effects to text and UI elements throughout the site. diff --git a/eleventy.config.js b/eleventy.config.js index 560fd98..037684a 100644 --- a/eleventy.config.js +++ b/eleventy.config.js @@ -96,6 +96,7 @@ export default async function (eleventyConfig) { eleventyConfig.addShortcode('svg', shortcodes.svgShortcode); eleventyConfig.addShortcode('image', shortcodes.imageShortcode); eleventyConfig.addShortcode('imageKeys', shortcodes.imageKeysShortcode); + eleventyConfig.addShortcode('animateText', shortcodes.animateText); eleventyConfig.addShortcode('year', () => `${new Date().getFullYear()}`); diff --git a/src/_config/events/build-css.js b/src/_config/events/build-css.js index 8c8d63c..1a9c431 100644 --- a/src/_config/events/build-css.js +++ b/src/_config/events/build-css.js @@ -16,7 +16,7 @@ const buildCss = async (inputPath, outputPaths) => { postcssImport, tailwindcss, autoprefixer, - cssnano + cssnano({preset: ['default', {discardUnused: false}]}) ]).process(inputContent, {from: inputPath}); for (const outputPath of outputPaths) { diff --git a/src/_config/plugins/markdown.js b/src/_config/plugins/markdown.js index dbad591..e227359 100644 --- a/src/_config/plugins/markdown.js +++ b/src/_config/plugins/markdown.js @@ -17,7 +17,11 @@ export const markdownLib = markdownIt({ typographer: true }) .disable('code') - .use(markdownItAttrs) + .use(markdownItAttrs, { + leftDelimiter: '{', + rightDelimiter: '}', + allowed: ['class', 'id', 'style'] + }) .use(markdownItPrism, { defaultLanguage: 'plaintext' }) diff --git a/src/_config/shortcodes.js b/src/_config/shortcodes.js index 4648bb6..3e3f8ea 100644 --- a/src/_config/shortcodes.js +++ b/src/_config/shortcodes.js @@ -1,4 +1,19 @@ import {imageShortcode, imageKeysShortcode} from './shortcodes/image.js'; import {svgShortcode} from './shortcodes/svg.js'; -export default {imageShortcode, imageKeysShortcode, svgShortcode}; +// Text animation shortcode - wraps each letter in a span with animation class +// Speed parameter scales animation duration: 0.5 = 2x slower, 2 = 2x faster (default: 1) +const animateText = (content, animation, speed = '1') => { + const letters = content.split(''); + + const letterSpans = letters + .map((letter) => { + if (letter === ' ') return ' '; + return `${letter}`; + }) + .join(''); + + return letterSpans; +}; + +export default {imageShortcode, imageKeysShortcode, svgShortcode, animateText}; diff --git a/src/assets/css/global/utilities/text-animations.css b/src/assets/css/global/utilities/text-animations.css index 115fa81..e7d22d8 100644 --- a/src/assets/css/global/utilities/text-animations.css +++ b/src/assets/css/global/utilities/text-animations.css @@ -1,6 +1,6 @@ -/* Hand-drawn animation effects for text and UI elements */ - -/* Shiver effect - subtle shake/vibration */ +/* ═══════════════════════════════════════════════════════════════════════ + SHIVER — Subtle vibration and micro-rotation + ═══════════════════════════════════════════════════════════════════════ */ @keyframes shiver { 0%, 100% { transform: translate(0, 0) rotate(0deg); @@ -34,12 +34,23 @@ } } -.shiver { +.text-shiver { display: inline-block; - animation: shiver 0.8s ease-in-out infinite; + animation: shiver 0.5s ease-in-out infinite; } -/* Wobble effect - gentle sway */ +.text-shiver:nth-child(2n) { + animation-delay: 0.1s; +} + +.text-shiver:nth-child(3n) { + animation-delay: 0.2s; +} + + +/* ═══════════════════════════════════════════════════════════════════════ + WOBBLE — Gentle sway with rotation + ═══════════════════════════════════════════════════════════════════════ */ @keyframes wobble { 0%, 100% { transform: rotate(0deg) translateY(0); @@ -52,32 +63,25 @@ } } -.wobble { +.text-wobble { display: inline-block; - animation: wobble 2s ease-in-out infinite; + animation: wobble 0.8s ease-in-out infinite; transform-origin: center bottom; } -/* Draw effect - simulate hand-drawing text */ -@keyframes draw-in { - from { - opacity: 0; - filter: blur(2px); - transform: translateX(-10px) rotate(-5deg); - } - to { - opacity: 1; - filter: blur(0); - transform: translateX(0) rotate(0deg); - } +.text-wobble:nth-child(2n) { + animation-delay: 0.3s; } -.draw { - display: inline-block; - animation: draw-in 0.6s cubic-bezier(0.68, -0.55, 0.27, 1.55) forwards; +.text-wobble:nth-child(3n) { + animation-delay: 0.6s; } -/* Jitter effect - erratic movement */ + + +/* ═══════════════════════════════════════════════════════════════════════ + JITTER — Erratic rapid movement + ═══════════════════════════════════════════════════════════════════════ */ @keyframes jitter { 0%, 100% { transform: translate(0, 0); } 10% { transform: translate(-2px, 1px); } @@ -91,12 +95,15 @@ 90% { transform: translate(-2px, -2px); } } -.jitter { +.text-jitter { display: inline-block; - animation: jitter 0.5s ease-in-out infinite; + animation: jitter 0.4s ease-in-out infinite; } -/* Bounce effect - playful bounce */ + +/* ═══════════════════════════════════════════════════════════════════════ + BOUNCE — Playful vertical bounce + ═══════════════════════════════════════════════════════════════════════ */ @keyframes bounce { 0%, 100% { transform: translateY(0); @@ -106,35 +113,111 @@ } } -.bounce { +.text-bounce { display: inline-block; animation: bounce 1s ease-in-out infinite; } -/* Stagger animations for multiple elements */ -.shiver:nth-child(2n) { + +/* ═══════════════════════════════════════════════════════════════════════ + BOBBING — Independent vertical movement with staggered timing + ═══════════════════════════════════════════════════════════════════════ */ +@keyframes bobbing { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-8px); + } +} + +.text-bob { + display: inline-block; + animation: bobbing 1.8s ease-in-out infinite; +} + +.text-bob:nth-child(2n) { animation-delay: 0.1s; } -.shiver:nth-child(3n) { - animation-delay: 0.2s; +.text-bob:nth-child(3n) { + animation-delay: 0.25s; } -.wobble:nth-child(2n) { +.text-bob:nth-child(4n) { + animation-delay: 0.4s; +} + +.text-bob:nth-child(5n) { + animation-delay: 0.55s; +} + + +/* ═══════════════════════════════════════════════════════════════════════ + WAVE — Letters move up and down in a flowing wave pattern + ═══════════════════════════════════════════════════════════════════════ */ +@keyframes wave { + 0% { + transform: translateY(0); + } + 12.5% { + transform: translateY(-2px); + } + 25% { + transform: translateY(-8px); + } + 37.5% { + transform: translateY(-12px); + } + 50% { + transform: translateY(-8px); + } + 62.5% { + transform: translateY(-2px); + } + 75% { + transform: translateY(0); + } + 87.5% { + transform: translateY(2px); + } + 100% { + transform: translateY(0); + } +} + +.text-wave { + display: inline-block; + animation: wave 1.2s ease-in-out infinite; +} + +.text-wave:nth-child(2n) { + animation-delay: 0.15s; +} + +.text-wave:nth-child(3n) { animation-delay: 0.3s; } -.wobble:nth-child(3n) { +.text-wave:nth-child(4n) { + animation-delay: 0.45s; +} + +.text-wave:nth-child(5n) { animation-delay: 0.6s; } -/* Reduce motion for accessibility */ + +/* ═══════════════════════════════════════════════════════════════════════ + ACCESSIBILITY — Respect motion preferences + ═══════════════════════════════════════════════════════════════════════ */ @media (prefers-reduced-motion: reduce) { - .shiver, - .wobble, - .draw, - .jitter, - .bounce { + .text-shiver, + .text-wobble, + .text-jitter, + .text-bounce, + .text-bob, + .text-wave { animation: none; } } diff --git a/src/pages/projects/mixes/tomorrowsbacon/00-album.md b/src/pages/projects/mixes/tomorrowsbacon/00-album.md index 2f53dec..5baee18 100644 --- a/src/pages/projects/mixes/tomorrowsbacon/00-album.md +++ b/src/pages/projects/mixes/tomorrowsbacon/00-album.md @@ -1,6 +1,6 @@ --- layout: mix -title: "Tomorrow’s Bacon" +title: "Tomorrow's Bacon" project: TomorrowsBacon permalink: /mixes/tomorrowsbacon/index.html go: tomorrowsbacon @@ -9,6 +9,20 @@ A digital mixed CD. Argument goes here, eventually. In the meantime, imagine that I pasted in some filler text. +{% animateText "shivering", "shiver" %} +{% animateText "wobbling", "wobble" %} +{% animateText "jittering", "jitter" %} +{% animateText "bouncing", "bounce" %} +{% animateText "bobbing up and down!", "bob" %} +{% animateText "wave motion!", "wave" %} +{% animateText "slower wave", "wave", "0.5" %} +{% animateText "fast shiver", "shiver", "2" %} +{% animateText "glacial", "wobble", "0.1" %} + +{% animateText "glacial", "shiver", "0.1" %} + + + {% set itemList = collections.mix %} {% set headingLevel = "h3" %} diff --git a/tailwind.config.js b/tailwind.config.js index 918a7e7..a9e9861 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -28,6 +28,14 @@ const spacing = tokensToTailwind(clampGenerator(spacingTokens.items)); export default { content: ['./src/**/*.{html,js,md,njk,liquid,webc}'], + safelist: [ + 'text-shiver', + 'text-wobble', + 'text-jitter', + 'text-bounce', + 'text-bob', + 'text-wave' + ], presets: [], theme: { screens: {