From 2f6b037cf25f531d435ab4d41cd8c132de161df1 Mon Sep 17 00:00:00 2001 From: abbasmir12 Date: Fri, 13 Mar 2026 18:13:50 +0000 Subject: [PATCH 1/2] feat(engineering-team): add epic-design skill with asset pipeline --- engineering-team/CLAUDE.md | 29 +- engineering-team/epic-design.zip | Bin 0 -> 54458 bytes engineering-team/epic-design/SKILL.md | 352 +++++++++ .../epic-design/references/accessibility.md | 378 ++++++++++ .../epic-design/references/asset-pipeline.md | 135 ++++ .../epic-design/references/depth-system.md | 361 +++++++++ .../references/directional-reveals.md | 455 +++++++++++ .../epic-design/references/examples.md | 344 +++++++++ .../references/inter-section-effects.md | 493 ++++++++++++ .../epic-design/references/motion-system.md | 531 +++++++++++++ .../epic-design/references/performance.md | 261 +++++++ .../epic-design/references/text-animations.md | 709 ++++++++++++++++++ .../epic-design/scripts/inspect-assets.py | 254 +++++++ .../epic-design/scripts/validate-layers.js | 165 ++++ 14 files changed, 4461 insertions(+), 6 deletions(-) create mode 100644 engineering-team/epic-design.zip create mode 100644 engineering-team/epic-design/SKILL.md create mode 100644 engineering-team/epic-design/references/accessibility.md create mode 100644 engineering-team/epic-design/references/asset-pipeline.md create mode 100644 engineering-team/epic-design/references/depth-system.md create mode 100644 engineering-team/epic-design/references/directional-reveals.md create mode 100644 engineering-team/epic-design/references/examples.md create mode 100644 engineering-team/epic-design/references/inter-section-effects.md create mode 100644 engineering-team/epic-design/references/motion-system.md create mode 100644 engineering-team/epic-design/references/performance.md create mode 100644 engineering-team/epic-design/references/text-animations.md create mode 100644 engineering-team/epic-design/scripts/inspect-assets.py create mode 100644 engineering-team/epic-design/scripts/validate-layers.js diff --git a/engineering-team/CLAUDE.md b/engineering-team/CLAUDE.md index dd56174..b70ccc5 100644 --- a/engineering-team/CLAUDE.md +++ b/engineering-team/CLAUDE.md @@ -4,17 +4,17 @@ This guide covers the 24 production-ready engineering skills and their Python au ## Engineering Skills Overview -**Core Engineering (13 skills):** +**Core Engineering (14 skills):** - senior-architect, senior-frontend, senior-backend, senior-fullstack - senior-qa, senior-devops, senior-secops - code-reviewer, senior-security -- aws-solution-architect, ms365-tenant-manager, google-workspace-cli, tdd-guide, tech-stack-evaluator +- aws-solution-architect, ms365-tenant-manager, google-workspace-cli, tdd-guide, tech-stack-evaluator, epic-design **AI/ML/Data (5 skills):** - senior-data-scientist, senior-data-engineer, senior-ml-engineer - senior-prompt-engineer, senior-computer-vision -**Total Tools:** 30+ Python automation tools +**Total Tools:** 32+ Python automation tools ## Core Engineering Tools @@ -287,6 +287,23 @@ services: --- -**Last Updated:** March 11, 2026 -**Skills Deployed:** 24 engineering skills production-ready -**Total Tools:** 35+ Python automation tools across core + AI/ML/Data +**Last Updated:** March 13, 2026 +**Skills Deployed:** 25 engineering skills production-ready +**Total Tools:** 37+ Python automation tools across core + AI/ML/Data + epic-design + +--- + +## epic-design + +Build cinematic 2.5D interactive websites with scroll storytelling, parallax depth, and premium animations. Includes asset inspection pipeline, 45+ techniques across 8 categories, and accessibility built-in. + +**Key features:** +- 6-layer depth system with automatic parallax +- 13 text animation techniques, 9 scroll patterns +- Asset inspection with background judgment rules +- Python tool for automated image analysis +- WCAG 2.1 AA compliant (reduced-motion) + +**Use for:** Product launches, portfolio sites, SaaS marketing pages, event sites, Apple-style animations + +**Live demo:** [epic-design-showcase.vercel.app](https://epic-design-showcase.vercel.app/) diff --git a/engineering-team/epic-design.zip b/engineering-team/epic-design.zip new file mode 100644 index 0000000000000000000000000000000000000000..978dea5fafb2eff849a3d84311fc2be756355c0c GIT binary patch literal 54458 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 literal 0 HcmV?d00001 diff --git a/engineering-team/epic-design/SKILL.md b/engineering-team/epic-design/SKILL.md new file mode 100644 index 0000000..3820e59 --- /dev/null +++ b/engineering-team/epic-design/SKILL.md @@ -0,0 +1,352 @@ +--- +name: epic-design +description: > + Build immersive, cinematic 2.5D interactive websites using scroll storytelling, + parallax depth, text animations, and premium scroll effects — no WebGL required. + Use this skill for any web design task: landing pages, product sites, hero sections, + scroll animations, parallax, sticky sections, section overlaps, floating products + between sections, clip-path reveals, text that flies in from sides, words that light + up on scroll, curtain drops, iris opens, card stacks, bleed typography, and any + site that should feel cinematic or premium. Trigger on phrases like "make it feel + alive", "Apple-style animation", "sections that overlap", "product rises between + sections", "immersive", "scrollytelling", or any scroll-driven visual effect. + Covers 45+ techniques across 8 categories. Always inspects, judges, and plans assets before coding. Use aggressively for ANY web design task. +license: MIT +metadata: + version: 1.0.0 + author: Epic Design Skill + category: engineering-team + updated: 2026-03-13 +--- + +# Epic Design Skill + +You are now a **world-class epic design expert**. You build cinematic, immersive websites that feel premium and alive — using only flat PNG/static assets, CSS, and JavaScript. No WebGL, no 3D modeling software required. + +## Before Starting + +**Check for context first:** +If `project-context.md` or `product-context.md` exists, read it before asking questions. Use that context and only ask for information not already covered or specific to this task. + +## Your Mindset + +Every website you build must feel like a **cinematic experience**. Think: Apple product pages, Awwwards winners, luxury brand sites. Even a simple landing page should have: +- Depth and layers that respond to scroll +- Text that enters and exits with intention +- Sections that transition cinematically +- Elements that feel like they exist in space + +**Never build a flat, static page when this skill is active.** + +--- + +## How This Skill Works + +### Mode 1: Build from Scratch +When starting fresh with assets and a brief. Follow the complete workflow below (Steps 1-5). + +### Mode 2: Enhance Existing Site +When adding 2.5D effects to an existing page. Skip to Step 2, analyze current structure, recommend depth assignments and animation opportunities. + +### Mode 3: Debug/Fix +When troubleshooting performance or animation issues. Use `scripts/validate-layers.js`, check GPU rules, verify reduced-motion handling. + +--- + +## Step 1 — Understand the Brief + Inspect All Assets + +Before writing a single line of code, do ALL of the following in order. + +### A. Extract the brief +1. What is the product/content? (brand site, portfolio, SaaS, event, etc.) +2. What mood/feeling? (dark/cinematic, bright/energetic, minimal/luxury, etc.) +3. How many sections? (hero only, full page, specific section?) + +### B. Inspect every uploaded image asset + +Run `scripts/inspect-assets.py` on every image the user has provided. +For each image, determine: + +1. **Format** — JPEG never has a real alpha channel. PNG may have a fake one. + +2. **Background status** — Use the script output. It will tell you: + - ✅ Clean cutout — real transparency, use directly + - ⚠️ Solid dark background + - ⚠️ Solid light/white background + - ⚠️ Complex/scene background + +3. **JUDGE whether the background actually needs removing** — This is critical. + Not every image with a background needs it removed. Ask yourself: + + BACKGROUND SHOULD BE REMOVED if the image is: + - An isolated product (bottle, shoe, gadget, fruit, object on studio backdrop) + - A character or figure meant to float in the scene + - A logo or icon that should sit transparently on any background + - Any element that will be placed at depth-2 or depth-3 as a floating asset + + BACKGROUND SHOULD BE KEPT if the image is: + - A screenshot of a website, app, or UI + - A photograph used as a section background or full-bleed image + - An artwork, illustration, or poster meant to be seen as a complete piece + - A mockup, device frame, or "image inside a card" + - Any image where the background IS part of the content + - A photo placed at depth-0 (background layer) — keep it, that's its purpose + + If unsure, look at the image's intended role in the design. If it needs to + "float" freely over other content → remove bg. If it fills a space or IS + the content → keep it. + +4. **Inform the user about every image** — whether bg is fine or not. + Use the exact format from `references/asset-pipeline.md` Step 4. + +5. **Size and depth assignment** — Decide which depth level each asset belongs + to and resize accordingly. State your decisions to the user before building. + +### C. Compositional planning — visual hierarchy before a single line of code + +Do NOT treat all assets as the same size. Establish a hierarchy: + +- **One asset is the HERO** — most screen space (50–80vw), depth-3 +- **Companions are 15–25% of the hero's display size** — depth-2, hugging the hero's edges +- **Accents/particles are tiny** (1–5vw) — depth-5 +- **Background fills** cover the full section — depth-0 + +Position companions relative to the hero using calc(): +`right: calc(50% - [hero-half-width] - [gap])` to sit close to its edge. + +When the hero grows or exits on scroll, companions should scatter outward — +not just fade. This reinforces that they were orbiting the hero. + +### D. Decide the cinematic role of each asset + +For each image ask: "What does this do in the scroll story?" +- Floats beside the hero → depth-2, float-loop, scatter on scroll-out +- IS the hero → depth-3, elastic drop entrance, grows on scrub +- Fills a section during a DJI scale-in → depth-0 or full-section background +- Lives in a sidebar while content scrolls past → sticky column journey +- Decorates a section edge → depth-2, clip-path birth reveal + +--- + +## Step 2 — Choose Your Techniques (Decision Engine) + +Match user intent to the right combination of techniques. Read the full technique details from `references/` files. + +### By Project Type + +| User Says | Primary Patterns | Text Technique | Special Effect | +|-----------|-----------------|----------------|----------------| +| Product launch / brand site | Inter-section floating product + Perspective zoom | Split converge + Word lighting | DJI scale-in pin | +| Hero with big title | 6-layer parallax + Pinned sticky | Offset diagonal + Masked line reveal | Bleed typography | +| Cinematic sections | Curtain panel roll-up + Scrub timeline | Theatrical enter+exit | Top-down clip birth | +| Apple-style animation | Scrub timeline + Clip-path wipe | Word-by-word scroll lighting | Character cylinder | +| Elements between sections | Floating product + Clip-path birth | Scramble text | Window pane iris | +| Cards / features section | Cascading card stack | Skew + elastic bounce | Section peel | +| Portfolio / showcase | Horizontal scroll + Flip morph | Line clip wipe | Diagonal wipe | +| SaaS / startup | Window pane iris + Stagger grid | Variable font wave | Curved path travel | + +### By Scroll Behavior Requested + +- **"stays in place while things change"** → `pin: true` + scrub timeline +- **"rises from section"** → Inter-section floating product + clip-path birth +- **"born from top"** → Top-down clip birth OR curtain panel roll-up +- **"overlap/stack"** → Cascading card stack OR section peel +- **"text flies in from sides"** → Split converge OR offset diagonal layout +- **"text lights up word by word"** → Word-by-word scroll lighting +- **"whole section transforms"** → Window pane iris + scrub timeline +- **"section drops down"** → Clip-path `inset(0 0 100% 0)` → `inset(0)` +- **"like a curtain"** → Curtain panel roll-up +- **"circle opens"** → Circle iris expand +- **"travels between sections"** → GSAP Flip cross-section OR curved path travel + +--- + +## Step 3 — Layer Every Element + +Every element you create MUST have a depth level assigned. This is non-negotiable. + +``` +DEPTH 0 → Far background | parallax: 0.10x | blur: 8px | scale: 0.70 +DEPTH 1 → Glow/atmosphere | parallax: 0.25x | blur: 4px | scale: 0.85 +DEPTH 2 → Mid decorations | parallax: 0.50x | blur: 0px | scale: 1.00 +DEPTH 3 → Main objects | parallax: 0.80x | blur: 0px | scale: 1.05 +DEPTH 4 → UI / text | parallax: 1.00x | blur: 0px | scale: 1.00 +DEPTH 5 → Foreground FX | parallax: 1.20x | blur: 0px | scale: 1.10 +``` + +Apply as: `data-depth="3"` on HTML elements, matching CSS class `.depth-3`. + +→ Full depth system details: `references/depth-system.md` + +--- + +## Step 4 — Apply Accessibility & Performance (Always) + +These are MANDATORY in every output: + +```css +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} +``` + +- Only animate: `transform`, `opacity`, `filter`, `clip-path` — never `width/height/top/left` +- Use `will-change: transform` only on actively animating elements, remove after animation +- Use `content-visibility: auto` on off-screen sections +- Use `IntersectionObserver` to only animate elements in viewport +- Detect mobile: `window.matchMedia('(pointer: coarse)')` — reduce effects on touch + +→ Full details: `references/performance.md` and `references/accessibility.md` + +--- + +## Step 5 — Code Structure (Always Use This HTML Architecture) + +```html + +
+ + + + + + + + +
+ + [description] +
+ +
+ +

Your Headline

+
+ + + +
+``` + +→ Full boilerplate: `assets/hero-section.html` +→ Full CSS system: `assets/hero-section.css` +→ Full JS engine: `assets/hero-section.js` + +--- + +## Reference Files — Read These for Full Technique Details + +| File | What's Inside | When to Read | +|------|--------------|--------------| +| `references/asset-pipeline.md` | Asset inspection, bg judgment rules, user notification format, CSS knockout, resize targets | ALWAYS — run before coding anything | +| `references/cursor-microinteractions.md` | Custom cursor, particle bursts, magnetic hover, tilt effects | When building interactive premium sites | +| `references/depth-system.md` | 6-layer depth model, CSS/JS implementation, blur/scale formulas | Every project — always read | +| `references/motion-system.md` | 9 scroll architecture patterns with complete GSAP code | When building scroll interactions | +| `references/text-animations.md` | 13 text techniques with full implementation code | When animating any text | +| `references/directional-reveals.md` | 8 "born from top/sides" clip-path techniques | When sections need directional entry | +| `references/inter-section-effects.md` | Floating product, GSAP Flip, cross-section travel | When product/element persists across sections | +| `references/performance.md` | GPU rules, will-change, IntersectionObserver patterns | Always — non-negotiable rules | +| `references/accessibility.md` | WCAG 2.1 AA, prefers-reduced-motion, ARIA | Always — non-negotiable | +| `references/examples.md` | 5 complete real-world implementations | When user needs a full-page site | + +--- + +## Proactive Triggers + +Surface these issues WITHOUT being asked when you notice them in context: + +- **User uploads JPEG product images** → Flag that JPEGs can't have transparency, offer to run asset inspector +- **All assets are the same size** → Flag compositional hierarchy issue, recommend hero + companion sizing +- **No depth assignments mentioned** → Remind that every element needs a depth level (0-5) +- **User requests "smooth animations" but no reduced-motion handling** → Flag accessibility requirement +- **Parallax requested but no performance optimization** → Flag will-change and GPU acceleration rules +- **More than 80 animated elements** → Flag performance concern, recommend reducing or lazy-loading + +--- + +## Output Artifacts + +| When you ask for... | You get... | +|---------------------|------------| +| "Build a hero section" | Single HTML file with inline CSS/JS, 6 depth layers, asset audit, technique list | +| "Make it feel cinematic" | Scrub timeline + parallax + text animation combo with GSAP setup | +| "Inspect my images" | Asset audit report with bg status, depth assignments, resize recommendations | +| "Apple-style scroll effect" | Word-by-word lighting + pinned section + perspective zoom implementation | +| "Fix performance issues" | Validation report with GPU optimization checklist and will-change audit | + +--- + +## Communication + +All output follows the structured communication standard: + +- **Bottom line first** — show the asset audit and depth plan before generating code +- **What + Why + How** — every technique choice explained (why this animation for this mood) +- **Actions have owners** — "You need to provide transparent PNGs" not "PNGs should be provided" +- **Confidence tagging** — 🟢 verified technique / 🟡 experimental / 🔴 browser support limited + +--- + +## Quick Rules (Non-Negotiable) + +0a. ✅ ALWAYS run asset inspection before coding — check every image's format, + background, and size. State depth assignments to the user before building. +0b. ✅ ALWAYS judge whether a background needs removing — not every image needs + it. Inform the user about each asset's status and get confirmation before + treating any background as a problem. Never auto-remove, never silently ignore. +1. ✅ Every section has minimum **3 depth layers** +2. ✅ Every text element uses at least **1 animation technique** +3. ✅ Every project includes **`prefers-reduced-motion`** fallback +4. ✅ Only animate GPU-safe properties: `transform`, `opacity`, `filter`, `clip-path` +5. ✅ Product images always assigned **depth-3** by default +6. ✅ Background images always **depth-0** with slight blur +7. ✅ Floating loops on any "hero" element (6–14s, never completely static) +8. ✅ Every decorative element gets `aria-hidden="true"` +9. ✅ Mobile gets reduced effects via `pointer: coarse` detection +10. ✅ `will-change` removed after animations complete + +--- + +## Output Format + +Always deliver: +1. **Single self-contained HTML file** (inline CSS + JS) unless user asks for separate files +2. **CDN imports** for GSAP via jsDelivr: `https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js` +3. **Comments** explaining every major section and technique used +4. **Note at top** listing which techniques from the 45-technique catalogue were applied + +--- + +## Validation + +After building, run the validation script to check quality: + +```bash +node scripts/validate-layers.js path/to/index.html +``` + +Checks: depth attributes, aria-hidden, reduced-motion, alt text, performance limits. + +--- + +## Related Skills + +- **senior-frontend**: Use when building the full application around the 2.5D site. NOT for the cinematic effects themselves. +- **ui-design**: Use when designing the visual layout and components. NOT for scroll animations or depth effects. +- **landing-page-generator**: Use for quick SaaS landing page scaffolds. NOT for custom cinematic experiences. +- **page-cro**: Use after the 2.5D site is built to optimize conversion. NOT during the initial build. +- **senior-architect**: Use when the 2.5D site is part of a larger system architecture. NOT for standalone pages. +- **accessibility-auditor**: Use to verify full WCAG compliance after build. This skill includes basic reduced-motion handling. diff --git a/engineering-team/epic-design/references/accessibility.md b/engineering-team/epic-design/references/accessibility.md new file mode 100644 index 0000000..14043d3 --- /dev/null +++ b/engineering-team/epic-design/references/accessibility.md @@ -0,0 +1,378 @@ +# Accessibility Reference + +## Non-Negotiable Rules + +Every 2.5D website MUST implement ALL of the following. These are not optional enhancements — they are legal requirements in many jurisdictions and ethical requirements always. + +--- + +## 1. prefers-reduced-motion (Most Critical) + +Parallax and complex animations can trigger vestibular disorders — dizziness, nausea, migraines — in a significant portion of users. WCAG 2.1 Success Criterion 2.3.3 requires handling this. + +```css +/* This block must be in EVERY project */ +@media (prefers-reduced-motion: reduce) { + /* Nuclear option: stop all animations globally */ + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } + + /* Specifically disable 2.5D techniques */ + .float-loop { animation: none !important; } + .parallax-layer { transform: none !important; } + .depth-0, .depth-1, .depth-2, + .depth-3, .depth-4, .depth-5 { + transform: none !important; + filter: none !important; + } + .glow-blob { opacity: 0.3; animation: none !important; } + .theatrical, .theatrical-with-exit { + animation: none !important; + opacity: 1 !important; + transform: none !important; + } +} +``` + +```javascript +// Also check in JavaScript — some GSAP animations don't respect CSS media queries +if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { + gsap.globalTimeline.timeScale(0); // Stops all GSAP animations + ScrollTrigger.getAll().forEach(t => t.kill()); // Kill all scroll triggers + + // Show all content immediately (don't hide-until-animated) + document.querySelectorAll('[data-animate]').forEach(el => { + el.style.opacity = '1'; + el.style.transform = 'none'; + el.removeAttribute('data-animate'); + }); +} +``` + +## Per-Effect Reduced Motion (Smarter Than Kill-All) + +Rather than freezing every animation globally, classify each type: + +| Animation Type | At reduced-motion | +|---|---| +| Scroll parallax depth layers | DISABLE — continuous motion triggers vestibular issues | +| Float loops / ambient movement | DISABLE — looping motion is a trigger | +| DJI scale-in / perspective zoom | DISABLE — fast scale can cause dizziness | +| Particle systems | DISABLE | +| Clip-path reveals (one-shot) | KEEP — not continuous, not fast | +| Fade-in on scroll (opacity only) | KEEP — safe | +| Word-by-word scroll lighting | KEEP — no movement, just colour | +| Curtain / wipe reveals (one-shot) | KEEP | +| Text entrance slides (one-shot) | KEEP but reduce duration | + +```javascript +const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + +if (prefersReduced) { + // Disable the motion-heavy ones + document.querySelectorAll('.float-loop').forEach(el => { + el.style.animation = 'none'; + }); + document.querySelectorAll('[data-depth]').forEach(el => { + el.style.transform = 'none'; + el.style.willChange = 'auto'; + }); + + // Slow GSAP to near-freeze (don't fully kill — keep structure intact) + gsap.globalTimeline.timeScale(0.01); + + // Safe animations: show them immediately at final state + gsap.utils.toArray('.clip-reveal, .fade-reveal, .word-light').forEach(el => { + gsap.set(el, { clipPath: 'inset(0 0% 0 0)', opacity: 1 }); + }); +} +``` + +--- + +## 2. Semantic HTML Structure + +```html + +
+ +
+ + + + + + + +
+ [Descriptive alt text — what is the product, what does it look like] + > +
+ +
+ +
+ +
+

Why Choose [Product]

+ +
+
+``` + +--- + +## 3. SplitText & Screen Readers + +When using SplitText to fragment text into characters/words, the individual fragments get announced one at a time by screen readers — which sounds terrible. Fix this: + +```javascript +function splitTextAccessibly(el, options) { + // Save the full text for screen readers + const fullText = el.textContent.trim(); + el.setAttribute('aria-label', fullText); + + // Split visually only + const split = new SplitText(el, options); + + // Hide the split fragments from screen readers + // Screen readers will use aria-label instead + split.chars?.forEach(char => char.setAttribute('aria-hidden', 'true')); + split.words?.forEach(word => word.setAttribute('aria-hidden', 'true')); + split.lines?.forEach(line => line.setAttribute('aria-hidden', 'true')); + + return split; +} + +// Usage +splitTextAccessibly(document.querySelector('.hero-title'), { type: 'chars,words' }); +``` + +--- + +## 4. Keyboard Navigation + +All interactive elements must be reachable and operable via keyboard (Tab, Enter, Space, Arrow keys). + +```css +/* Ensure focus indicators are visible — WCAG 2.4.7 */ +:focus-visible { + outline: 3px solid #005fcc; /* High contrast focus ring */ + outline-offset: 3px; + border-radius: 3px; +} + +/* Remove default outline only if replacing with custom */ +:focus:not(:focus-visible) { + outline: none; +} + +/* Skip link for keyboard users to bypass navigation */ +.skip-link { + position: absolute; + top: -100px; + left: 0; + background: #005fcc; + color: white; + padding: 12px 20px; + z-index: 10000; + font-weight: 600; + text-decoration: none; +} +.skip-link:focus { + top: 0; /* Appears at top when focused */ +} +``` + +```html + + +
+ ... +
+``` + +--- + +## 5. Color Contrast (WCAG 2.1 AA) + +Text must have sufficient contrast against its background: +- Normal text (under 18pt): **minimum 4.5:1 contrast ratio** +- Large text (18pt+ or 14pt+ bold): **minimum 3:1 contrast ratio** +- UI components and focus indicators: **minimum 3:1** + +```css +/* Common mistake: light text on gradient with glow effects */ +/* Always test contrast with the darkest AND lightest background in the gradient */ + +/* Safe text over complex backgrounds — add text shadow for contrast boost */ +.hero-text-on-image { + color: #ffffff; + /* Multiple small text shadows create a halo that boosts contrast */ + text-shadow: + 0 0 20px rgba(0,0,0,0.8), + 0 2px 4px rgba(0,0,0,0.6), + 0 0 40px rgba(0,0,0,0.4); +} + +/* Or use a semi-transparent backdrop */ +.text-backdrop { + background: rgba(0, 0, 0, 0.55); + backdrop-filter: blur(8px); + padding: 1rem 1.5rem; + border-radius: 8px; +} +``` + +**Testing tool:** Use browser DevTools accessibility panel or webaim.org/resources/contrastchecker/ + +--- + +## 6. Motion-Sensitive Users — User Control + +Beyond `prefers-reduced-motion`, provide an in-page control: + +```html + + +``` + +```javascript +const motionToggle = document.querySelector('.motion-toggle'); +let animationsEnabled = !window.matchMedia('(prefers-reduced-motion: reduce)').matches; + +motionToggle.addEventListener('click', () => { + animationsEnabled = !animationsEnabled; + motionToggle.setAttribute('aria-pressed', !animationsEnabled); + motionToggle.querySelector('.motion-toggle-text').textContent = + animationsEnabled ? 'Animations On' : 'Animations Off'; + + if (animationsEnabled) { + document.documentElement.classList.remove('no-motion'); + gsap.globalTimeline.timeScale(1); + } else { + document.documentElement.classList.add('no-motion'); + gsap.globalTimeline.timeScale(0); + } + + // Persist preference + localStorage.setItem('motionPreference', animationsEnabled ? 'on' : 'off'); +}); + +// Restore on load +const saved = localStorage.getItem('motionPreference'); +if (saved === 'off') motionToggle.click(); +``` + +--- + +## 7. Images — Alt Text Guidelines + +```html + +Tall glass of fresh orange juice with ice, floating on a gradient background + + + + + + + +Learn More + + + + +``` + +--- + +## 8. Loading Screen Accessibility + +```javascript +// Announce loading state to screen readers +function announceLoading() { + const announcement = document.createElement('div'); + announcement.setAttribute('role', 'status'); + announcement.setAttribute('aria-live', 'polite'); + announcement.setAttribute('aria-label', 'Page loading'); + announcement.className = 'sr-only'; // visually hidden + document.body.appendChild(announcement); + + // Update announcement when done + window.addEventListener('load', () => { + announcement.textContent = 'Page loaded'; + setTimeout(() => announcement.remove(), 1000); + }); +} +``` + +```css +/* Screen-reader only utility class */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0,0,0,0); + white-space: nowrap; + border: 0; +} +``` + +--- + +## WCAG 2.1 AA Compliance Checklist + +Before shipping any 2.5D website: + +- [ ] `prefers-reduced-motion` CSS block present and tested +- [ ] GSAP animations stopped when reduced motion detected +- [ ] All decorative elements have `aria-hidden="true"` +- [ ] All meaningful images have descriptive alt text +- [ ] SplitText elements have `aria-label` on parent +- [ ] Heading hierarchy is logical (h1 → h2 → h3, no skipping) +- [ ] All interactive elements reachable via keyboard Tab +- [ ] Focus indicators visible and have 3:1 contrast +- [ ] Skip-to-main-content link present +- [ ] Text contrast meets 4.5:1 minimum +- [ ] CTA buttons have descriptive text +- [ ] Motion toggle button provided (optional but recommended) +- [ ] Page has `` (or correct language) +- [ ] `
` landmark wraps page content +- [ ] Section landmarks use `aria-label` to differentiate them diff --git a/engineering-team/epic-design/references/asset-pipeline.md b/engineering-team/epic-design/references/asset-pipeline.md new file mode 100644 index 0000000..a318574 --- /dev/null +++ b/engineering-team/epic-design/references/asset-pipeline.md @@ -0,0 +1,135 @@ +# Asset Pipeline Reference + +Every image asset must be inspected and judged before use in any 2.5D site. +The AI inspects, judges, and informs — it does NOT auto-remove backgrounds. + +--- + +## Step 1 — Run the Inspection Script + +Run `scripts/inspect-assets.py` on every uploaded image before doing anything else. +The script outputs the format, mode, size, background type, and a recommendation +for each image. Read its output carefully. + +--- + +## Step 2 — Judge Whether Background Removal Is Actually Needed + +The script detects whether a background exists. YOU must decide whether it matters. + +### Remove the background if the image is: +- An isolated product on a studio backdrop (bottle, shoe, phone, fruit, object) +- A character or figure that needs to float in the scene +- A logo or icon placed at any depth layer +- Any element at depth-2 or depth-3 that needs to "float" over other content +- An asset where the background colour will visibly clash with the site background + +### Keep the background if the image is: +- A screenshot of a website, app UI, dashboard, or software +- A photograph used as a section background or depth-0 fill +- An artwork, poster, or illustration that is viewed as a complete piece +- A device mockup or "image inside a card/frame" design element +- A photo where the background is part of the visual content +- Any image placed at depth-0 — it IS the background, keep it + +### When unsure — ask the role: +> "Does this image need to float freely over other content?" +> Yes → remove bg. No → keep it. + +--- + +## Step 3 — Resize to Depth-Appropriate Dimensions + +Run the resize step in `scripts/inspect-assets.py` or do it manually. +Never embed a large image when a smaller one is sufficient. + +| Depth | Role | Max Longest Edge | +|---|---|---| +| 0 | Background fill | 1920px | +| 1 | Glow / atmosphere | 800px | +| 2 | Mid decorations, companions | 400px | +| 3 | Hero product | 1200px | +| 4 | UI components | 600px | +| 5 | Particles, sparkles | 128px | + +--- + +## Step 4 — Inform the User (Required for Every Asset) + +Before outputting any HTML, always show an asset audit to the user. + +For each image that has a background issue, use this exact format: + +> ⚠️ **Asset Notice — [filename]** +> +> This is a [JPEG / PNG] with a solid [black / white / coloured] background. +> As-is, it will appear as a visible box on the page rather than a floating asset. +> +> Based on its intended role ([product shot / decoration / etc.]), I think the +> background [should be removed / should be kept because it's a [screenshot/artwork/bg fill/etc.]]. +> +> **Options:** +> 1. Provide a new PNG with a transparent background — best quality, ideal +> 2. Proceed as-is with a CSS workaround (mix-blend-mode) — quick but approximate +> 3. Keep the background — if this image is meant to be seen with its background +> +> Which do you prefer? + +For clean images, confirm them briefly: + +> ✅ **[filename]** — clean transparent PNG, resized to [X]px, assigned depth-[N] ([role]) + +Show all of this BEFORE outputting HTML. Wait for the user's response on any ⚠️ items. + +--- + +## Step 5 — CSS Workaround (Only After User Approves) + +Apply ONLY if the user explicitly chooses option 2 above: + +```css +/* Dark background image on a dark site — black pixels become invisible */ +.on-dark-bg { + mix-blend-mode: screen; +} + +/* Light background image on a light site — white pixels become invisible */ +.on-light-bg { + mix-blend-mode: multiply; +} +``` + +Always add a comment in the HTML when using this: +```html + +``` + +Limitations: +- `screen` lightens mid-tones — only works well on very dark site backgrounds +- `multiply` darkens mid-tones — only works well on very light site backgrounds +- Neither works on complex or gradient backgrounds +- A proper cutout PNG always gives better results + +--- + +## Step 6 — CSS Rules for Transparent Images + +Whether the image came in clean or had its background resolved, always apply: + +```css +/* ALWAYS use drop-shadow — it follows the actual pixel shape */ +.product-img { + filter: drop-shadow(0 30px 60px rgba(0, 0, 0, 0.4)); +} + +/* NEVER use box-shadow on cutout images — it creates a rectangle, not a shape shadow */ + +/* NEVER apply these to transparent/cutout images: */ +/* + border-radius → clips transparency into a rounded box + overflow: hidden → same problem on the parent element + object-fit: cover → stretches image to fill a box, destroys the cutout + background-color → makes the bounding box visible +*/ +``` diff --git a/engineering-team/epic-design/references/depth-system.md b/engineering-team/epic-design/references/depth-system.md new file mode 100644 index 0000000..f146f58 --- /dev/null +++ b/engineering-team/epic-design/references/depth-system.md @@ -0,0 +1,361 @@ +# Depth System Reference + +The 2.5D illusion is built entirely on a **6-level depth model**. Every element on the page belongs to exactly one depth level. Depth controls four automatic properties: parallax speed, blur, scale, and shadow intensity. Together these four signals trick the human visual system into perceiving genuine spatial depth from flat assets. + +--- + +## The 6-Level Depth Table + +| Level | Name | Parallax | Blur | Scale | Shadow | Z-Index | +|-------|-------------------|----------|-------|-------|---------|---------| +| 0 | Far Background | 0.10x | 8px | 0.70 | 0.05 | 0 | +| 1 | Glow / Atmosphere | 0.25x | 4px | 0.85 | 0.10 | 1 | +| 2 | Mid Decorations | 0.50x | 0px | 1.00 | 0.20 | 2 | +| 3 | Main Objects | 0.80x | 0px | 1.05 | 0.35 | 3 | +| 4 | UI / Text | 1.00x | 0px | 1.00 | 0.00 | 4 | +| 5 | Foreground FX | 1.20x | 0px | 1.10 | 0.50 | 5 | + +**Parallax formula:** +``` +element_translateY = scroll_position * depth_factor * -1 +``` +A depth-0 element at scroll position 500px moves only -50px (barely moves — feels far away). +A depth-5 element at 500px moves -600px (moves fast — feels close). + +--- + +## CSS Implementation + +### CSS Custom Properties Foundation +```css +:root { + /* Depth parallax factors */ + --depth-0-factor: 0.10; + --depth-1-factor: 0.25; + --depth-2-factor: 0.50; + --depth-3-factor: 0.80; + --depth-4-factor: 1.00; + --depth-5-factor: 1.20; + + /* Depth blur values */ + --depth-0-blur: 8px; + --depth-1-blur: 4px; + --depth-2-blur: 0px; + --depth-3-blur: 0px; + --depth-4-blur: 0px; + --depth-5-blur: 0px; + + /* Depth scale values */ + --depth-0-scale: 0.70; + --depth-1-scale: 0.85; + --depth-2-scale: 1.00; + --depth-3-scale: 1.05; + --depth-4-scale: 1.00; + --depth-5-scale: 1.10; + + /* Live scroll value (updated by JS) */ + --scroll-y: 0; +} + +/* Base layer class */ +.layer { + position: absolute; + inset: 0; + will-change: transform; + transform-origin: center center; +} + +/* Depth-specific classes */ +.depth-0 { + filter: blur(var(--depth-0-blur)); + transform: scale(var(--depth-0-scale)) + translateY(calc(var(--scroll-y) * var(--depth-0-factor) * -1px)); + z-index: 0; +} +.depth-1 { + filter: blur(var(--depth-1-blur)); + transform: scale(var(--depth-1-scale)) + translateY(calc(var(--scroll-y) * var(--depth-1-factor) * -1px)); + z-index: 1; + mix-blend-mode: screen; /* glow layers blend additively */ +} +.depth-2 { + transform: scale(var(--depth-2-scale)) + translateY(calc(var(--scroll-y) * var(--depth-2-factor) * -1px)); + z-index: 2; +} +.depth-3 { + transform: scale(var(--depth-3-scale)) + translateY(calc(var(--scroll-y) * var(--depth-3-factor) * -1px)); + z-index: 3; + filter: drop-shadow(0 20px 40px rgba(0,0,0,0.35)); +} +.depth-4 { + transform: translateY(calc(var(--scroll-y) * var(--depth-4-factor) * -1px)); + z-index: 4; +} +.depth-5 { + transform: scale(var(--depth-5-scale)) + translateY(calc(var(--scroll-y) * var(--depth-5-factor) * -1px)); + z-index: 5; +} +``` + +### JavaScript — Scroll Driver +```javascript +// Throttled scroll listener using requestAnimationFrame +let ticking = false; +let lastScrollY = 0; + +function updateDepthLayers() { + const scrollY = window.scrollY; + document.documentElement.style.setProperty('--scroll-y', scrollY); + ticking = false; +} + +window.addEventListener('scroll', () => { + lastScrollY = window.scrollY; + if (!ticking) { + requestAnimationFrame(updateDepthLayers); + ticking = true; + } +}, { passive: true }); +``` + +--- + +## Asset Assignment Rules + +### What Goes in Each Depth Level + +**Depth 0 — Far Background** +- Full-width background images (sky, gradient, texture) +- Very large PNGs (1920×1080+), file size 80–150KB max +- Heavily blurred by CSS — low detail is fine and preferred +- Examples: skyscape, abstract color wash, noise texture + +**Depth 1 — Glow / Atmosphere** +- Radial gradient blobs, lens flare PNGs, soft light overlays +- Size: 600–1000px, file size: 30–60KB max +- Always use `mix-blend-mode: screen` or `mix-blend-mode: lighten` +- Always `filter: blur(40px–100px)` applied on top of CSS blur +- Examples: orange glow blob behind product, atmospheric haze + +**Depth 2 — Mid Decorations** +- Abstract shapes, geometric patterns, floating decorative elements +- Size: 200–400px, file size: 20–50KB max +- Moderate shadow, no blur +- Examples: floating geometric shapes, brand pattern elements + +**Depth 3 — Main Objects (The Star)** +- Hero product images, characters, featured illustrations +- Size: 800–1200px, file size: 50–120KB max +- High detail, clean cutout (transparent PNG background) +- Strong drop shadow: `filter: drop-shadow(0 30px 60px rgba(0,0,0,0.4))` +- This is the element users look at — give it the most visual weight +- Examples: juice bottle, product shot, hero character + +**Depth 4 — UI / Text** +- Headlines, body copy, buttons, cards, navigation +- Always crisp, never blurred +- Text elements get animation data attributes (see text-animations.md) +- Examples: `

`, `

`, `