robert 3 dní pred
rodič
commit
3a7680c156

+ 186 - 2
README.md

@@ -1,8 +1,192 @@
 # xjs
 
-这个库用来最小化的集成一个可用的js库,用于引用在一个网站中的组件,兼容app、自适应
+一个**“最小可用、可直接用 `<script>` 引入”**的现代化前端工具库,面向 **网站 / H5 / App WebView** 的轻量组件集成场景。
 
+核心设计理念是 **"All in One Selector"** —— 一个全局函数 `animal` (可别名为 `$`) 既是选择器,也是功能入口。
 
-# 弹出层
+## ✨ 核心特性
 
+1.  **极简集成**: 零构建工具依赖,直接引入即可使用。
+2.  **统一入口**: 使用 `animal('.selector')` 统一管理动画与交互。
+3.  **高性能**: 优先使用 WAAPI (Web Animations API) 运行在合成线程。
+4.  **模块化**: 包含 **Animal** (动画) 和 **Layer** (弹窗) 两个核心能力。
 
+---
+
+## 📦 安装与引入
+
+```html
+<!-- 1. 引入动画引擎 (导出全局变量 animal) -->
+<script src="animal.js"></script>
+
+<!-- 2. 引入弹窗组件 (导出全局变量 Layer) -->
+<script src="layer.js"></script>
+```
+
+---
+
+## 🚀 快速上手
+
+为了获得最佳体验,建议将 `animal` 赋值给 `$` (或其他你喜欢的简写)。
+
+```javascript
+// 建立简写别名
+const $ = animal;
+```
+
+### 1. 基础动画 (`.animate`)
+
+```javascript
+$('.box').animate({
+  x: 200,             // 自动处理 transform
+  scale: 1.5,
+  opacity: 0.5,
+  backgroundColor: '#f00', 
+  duration: 800,
+  easing: 'ease-out'
+}).then(() => {
+  console.log('Animation done');
+});
+```
+
+### 2. 弹窗交互 (`.layer`)
+
+直接在选择器上绑定点击弹窗事件。
+
+```javascript
+// 点击按钮触发弹窗
+$('.btn-delete').layer({
+  title: '确定删除?',
+  text: '此操作不可恢复',
+  icon: 'warning',
+  showCancelButton: true
+});
+```
+
+### 3. SVG 描边 (`.draw`)
+
+```javascript
+$('path').draw({ 
+  duration: 1500,
+  easing: 'ease-in-out'
+});
+```
+
+---
+
+## 📚 详细功能
+
+### Animal: 动画引擎
+
+#### 物理弹簧 (Spring)
+无需配置复杂的贝塞尔曲线,使用物理参数即可。
+
+```javascript
+$('.card').animate({
+  y: [100, 0],
+  easing: { stiffness: 200, damping: 10 }
+});
+```
+
+#### 视口触发 (InView)
+元素进入屏幕可视区域时自动播放。
+
+```javascript
+$('.fade-up').inViewAnimate({
+  y: [50, 0],
+  opacity: [0, 1],
+  duration: 600
+}, { once: true });
+```
+
+#### 滚动驱动 (Scroll)
+将动画进度绑定到页面滚动条。
+
+```javascript
+const controls = $('.progress').animate({ 
+  width: ['0%', '100%'], 
+  autoplay: false 
+});
+
+// 使用静态方法 $.scroll 绑定
+$.scroll(controls);
+```
+
+#### 时间轴 (Timeline)
+编排串行复杂的动画序列。
+
+```javascript
+const tl = $.timeline();
+
+tl.add('.box-1', { x: 100 })
+  .add('.box-2', { x: 100 }, '-=200') // 提前 200ms
+  .play();
+```
+
+---
+
+### Layer: 弹出层
+
+虽然推荐使用 `$('.el').layer(...)` 绑定事件,但你也完全可以通过全局对象手动调用。
+
+#### 基础弹窗 (直接调用 $.layer)
+```javascript
+$.layer({
+  title: 'Hello World',
+  text: 'This is a message from $.layer()'
+});
+```
+
+#### Confirm 确认流程
+```javascript
+$.layer({
+  title: '提交表单?',
+  showCancelButton: true
+}).then((res) => {
+  if (res.isConfirmed) {
+    $.layer({ title: '提交成功!', icon: 'success' });
+  }
+});
+```
+
+#### 弹窗内容支持 DOM/HTML
+```javascript
+// 传入 DOM 元素
+$.layer({
+  title: 'Custom Content',
+  content: document.querySelector('#my-template')
+});
+
+// 或者传入 HTML 字符串
+$.layer({
+  html: '<strong>Bold Text</strong><br>New line'
+});
+```
+
+#### 配置参数表
+| 参数名 | 说明 |
+| :--- | :--- |
+| `title` | 标题 |
+| `text` | 正文 (纯文本) |
+| `html` | 正文 (HTML 字符串) |
+| `content` | 正文 (DOM 元素或 CSS 选择器) |
+| `icon` | `success` / `error` / `warning` |
+| `showCancelButton` | 是否显示取消按钮 (默认 false) |
+| `confirmButtonText` | 确认按钮文字 (默认 OK) |
+| `cancelButtonText` | 取消按钮文字 (默认 Cancel) |
+
+---
+
+## 🛠️ API 速查表
+
+| 调用方式 | 描述 |
+| :--- | :--- |
+| **`$(selector)`** | **核心选择器**,返回 Selection 对象 |
+| `Selection.animate(params)` | 执行动画 |
+| `Selection.draw(params)` | SVG 描边动画 |
+| `Selection.layer(options)` | 绑定点击弹窗 |
+| `Selection.inViewAnimate(...)` | 进入视口触发动画 |
+| **`$.spring(config)`** | 创建物理缓动函数 |
+| **`$.timeline()`** | 创建时间轴 |
+| **`$.scroll(controls)`** | 绑定滚动驱动 |
+| **`$.layer(options)`** | **直接弹出窗口 (别名 $.fire)** |

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 885 - 164
animal.js


+ 20 - 14
doc/animal.html

@@ -50,14 +50,14 @@
         <h2>1. Basic Transform & Opacity (WAAPI)</h2>
         <div class="box box-1"></div>
         <button class="btn" onclick="runBasic()">Animate</button>
-        <pre>animal.animate('.box-1', { x: 200, rotate: 180, opacity: 0.5, duration: 1000 })</pre>
+        <pre>$('.box-1').animate({ x: 200, rotate: 180, opacity: 0.5, duration: 1000 })</pre>
     </section>
 
     <section>
         <h2>2. Spring Physics</h2>
         <div class="box box-2"></div>
         <button class="btn" onclick="runSpring()">Spring</button>
-        <pre>animal.animate('.box-2', { x: 200, easing: { stiffness: 200, damping: 10 } })</pre>
+        <pre>$('.box-2').animate({ x: 200, easing: { stiffness: 200, damping: 10 } })</pre>
     </section>
 
     <section>
@@ -97,9 +97,12 @@
 
     <script src="../animal.js"></script>
     <script>
+        // Alias
+        const $ = animal;
+
         // 1. Basic
         function runBasic() {
-            animal.animate('.box-1', { 
+            $('.box-1').animate({ 
                 x: 200, 
                 rotate: 180, 
                 opacity: 0.5, 
@@ -108,13 +111,13 @@
             }).then(() => {
                 console.log('Basic finished');
                 // Chain back
-                animal.animate('.box-1', { x: 0, rotate: 0, opacity: 1, duration: 500 });
+                $('.box-1').animate({ x: 0, rotate: 0, opacity: 1, duration: 500 });
             });
         }
 
         // 2. Spring
         function runSpring() {
-            animal.animate('.box-2', { 
+            $('.box-2').animate({ 
                 x: 200, 
                 easing: { stiffness: 150, damping: 8 } 
             });
@@ -122,16 +125,17 @@
 
         // 3. Timeline
         function runTimeline() {
-            const tl = animal.timeline();
+            const tl = $.timeline();
             tl.add('.box-3a', { x: 100, duration: 500 })
               .add('.box-3b', { x: 100, rotate: 90, duration: 800 }, "-=200") // Overlap
               .add('.box-3a', { x: 0, duration: 500 })
-              .add('.box-3b', { x: 0, rotate: 0, duration: 500 });
+              .add('.box-3b', { x: 0, rotate: 0, duration: 500 })
+              .play();
         }
 
         // 4. SVG
         function runSvg() {
-            animal.svgDraw('.path-1', { duration: 1500, easing: 'ease-in-out' });
+            $('.path-1').draw({ duration: 1500, easing: 'ease-in-out' });
         }
 
         // 5. In View (Trigger)
@@ -140,26 +144,28 @@
         cards.forEach((card, i) => {
              // Alternate left/right entrance
              const xStart = i % 2 === 0 ? -100 : 100;
-             animal.inViewAnimate(card, {
+             // inViewAnimate() returns cleanup; call it if you use once:false or if you need to teardown manually.
+             $(card).inViewAnimate({
                 x: [xStart, 0],
                 opacity: [0, 1],
                 scale: [0.8, 1],
                 duration: 1000,
-                easing: { stiffness: 100, damping: 15 }
+                easing: { stiffness: 100, damping: 15 },
+                springFrames: 60
              }, { threshold: 0.2 });
         });
 
         // 6. Scroll Linked
         // 1. Progress Bar (Page Scroll)
-        const progressAnim = animal.animate('.progress-bar', {
+        const progressAnim = $('.progress-bar').animate({
             width: ['0%', '100%'],
             duration: 1000, // Duration doesn't matter much for scroll linked, just maps 0-1
             autoplay: false
         });
-        animal.scroll(progressAnim); // Default: window scroll
+        $.scroll(progressAnim); // Default: window scroll
 
         // 2. Box Rotation (Element Scroll Visibility)
-        const boxAnim = animal.animate('.scroll-box', {
+        const boxAnim = $('.scroll-box').animate({
             rotate: 360,
             x: 200,
             backgroundColor: '#e74c3c',
@@ -171,7 +177,7 @@
         // animal.js current `scroll` implementation supports `target` for element visibility progress!
         
         // Let's link it to the page scroll for clarity
-        animal.scroll(boxAnim);
+        $.scroll(boxAnim);
 
     </script>
 </body>

+ 17 - 41
doc/animal_basic.html

@@ -3,7 +3,7 @@
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>CSS Selector - Anime.js</title>
+    <title>CSS Selector - Animal.js</title>
     <style>
         :root {
             --bg-color: #111;
@@ -213,11 +213,11 @@
 
             <!-- JS Code View -->
             <div id="js-code" class="code-view active">
-<span class="kwd">import</span> <span class="punc">{</span> animate <span class="punc">}</span> <span class="kwd">from</span> <span class="str">'animejs'</span><span class="punc">;</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
 
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'.square'</span><span class="punc">,</span> <span class="punc">{</span> x<span class="punc">:</span> <span class="str">'17rem'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'#css-selector-id'</span><span class="punc">,</span> <span class="punc">{</span> rotate<span class="punc">:</span> <span class="str">'1turn'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'.row:nth-child(3) .square'</span><span class="punc">,</span> <span class="punc">{</span> scale<span class="punc">:</span> <span class="punc">[</span><span class="num">1</span><span class="punc">,</span> <span class="num">.5</span><span class="punc">,</span> <span class="num">1</span><span class="punc">]</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+<span class="fun">$</span><span class="punc">(</span><span class="str">'.square'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span> x<span class="punc">:</span> <span class="str">'17rem'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+<span class="fun">$</span><span class="punc">(</span><span class="str">'#css-selector-id'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span> rotate<span class="punc">:</span> <span class="str">'1turn'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+<span class="fun">$</span><span class="punc">(</span><span class="str">'.row:nth-child(3) .square'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span> scale<span class="punc">:</span> <span class="punc">[</span><span class="num">1</span><span class="punc">,</span> <span class="num">.5</span><span class="punc">]</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
             </div>
 
             <!-- HTML Code View -->
@@ -277,57 +277,33 @@
                 el.style.transform = 'none';
             });
             
-            // To faithfully replicate the demo where animations appear independent
-            // but the code selector is broad ('.square'), we need to be careful.
-            // If we run animal.animate('.square', ...) it targets ALL squares.
-            // In the visual demo, likely all squares move RIGHT.
-            // Then the second one ALSO rotates.
-            // Then the third one ALSO scales.
-            // Let's implement exactly what the code says.
+            const $ = animal;
             
             // 1. Move all squares
-            animal.animate('.square', { 
+            $('.square').animate({ 
                 x: '17rem', 
                 duration: 1000,
                 easing: 'ease-out'
             });
 
             // 2. Rotate the ID one
-            animal.animate('#css-selector-id', { 
+            $('#css-selector-id').animate({ 
                 rotate: '1turn', 
                 duration: 1000,
                 easing: 'ease-out'
             });
 
             // 3. Scale the 3rd row square
-            // Note: The HTML structure in demo-visual matches the selectors:
-            // .medium.row -> .square
-            // We use :nth-child(3) on the ROW, then find .square inside it.
-            // But wait, in .demo-visual, the rows are children of .demo-visual.
-            // row 1 is child 1
-            // row 2 is child 2
-            // row 3 is child 3
-            // So .medium.row:nth-child(3) should work relative to .demo-visual container if we scope it 
-            // or if we use the global document selector properly.
-            
-            // However, document.querySelectorAll('.row:nth-child(3)') might pick up other rows on the page if any.
-            // The class is "medium row".
-            // Let's use the specific selector from the code example.
-            
-            // We need to be careful about ":nth-child(3)". 
-            // The .medium.row elements are inside .demo-visual.
-            // If .demo-visual has other children (like text nodes or comments), index might differ.
-            // But browsers usually count element children for querySelector.
-            
-            // Let's tweak the selector to ensure it hits OUR 3rd row.
-            // The code example says: '.row:nth-child(3) .square'
-            // In our HTML: <div class="medium row"> is the class.
-            // So '.row' works if it has that class.
-            
-            animal.animate('.medium.row:nth-child(3) .square', { 
-                scale: [1, .5, 1], 
-                duration: 1000,
+            $('.medium.row:nth-child(3) .square').animate({
+                scale: [1, .5],
+                duration: 500,
                 easing: 'ease-in-out'
+            }).then(() => {
+                $('.medium.row:nth-child(3) .square').animate({
+                    scale: [.5, 1],
+                    duration: 500,
+                    easing: 'ease-in-out'
+                });
             });
         }
         

+ 6 - 8
doc/animatable_properties/test_css_props.html

@@ -35,7 +35,9 @@
             </div>
 
             <div id="js-code" class="code-view active">
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'.prop-box'</span><span class="punc">,</span> <span class="punc">{</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
+
+<span class="fun">$</span><span class="punc">(</span><span class="str">'.prop-box'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span>
   width<span class="punc">:</span> <span class="str">'100px'</span><span class="punc">,</span>
   backgroundColor<span class="punc">:</span> <span class="str">'#FFF'</span><span class="punc">,</span>
   borderRadius<span class="punc">:</span> <span class="str">'50%'</span>
@@ -76,15 +78,11 @@
             el.style.backgroundColor = ''; // Reset to CSS var
             el.style.borderRadius = '4px';
             
-            // Note: animal.js supports basic transforms/opacity via WAAPI.
-            // For other CSS props like width/color, it falls back to JS interpolation if implemented,
-            // or WAAPI if supported by browser.
-            // animal.js "lite" implementation mostly focuses on transforms/opacity in WAAPI path.
-            // The JS fallback in animal.js handles other props!
+            const $ = animal;
             
-            animal.animate('.prop-box', { 
+            $('.prop-box').animate({ 
                 width: '100px',
-                backgroundColor: '#FFF', // Color interpolation might need specific handling or rely on browser WAAPI if keys match
+                backgroundColor: '#FFF', 
                 borderRadius: '50%',
                 duration: 1000,
                 easing: 'ease-in-out'

+ 10 - 24
doc/animatable_properties/test_css_vars.html

@@ -29,8 +29,11 @@
                 <div class="tabs"><div class="tab active">JavaScript</div></div>
             </div>
             <div id="js-code" class="code-view active">
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'.box'</span><span class="punc">,</span> <span class="punc">{</span>
-  <span class="str">'--box-width'</span><span class="punc">:</span> <span class="str">'200px'</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
+<span class="fun">$</span><span class="punc">(</span><span class="str">'.box'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">(</span><span class="punc">{</span>
+  <span class="str">'--box-width'</span><span class="punc">:</span> <span class="str">'200px'</span><span class="punc">,</span>
+  duration<span class="punc">:</span> <span class="num">1000</span><span class="punc">,</span>
+  easing<span class="punc">:</span> <span class="str">'ease-in-out'</span>
 <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
             </div>
             <div class="demo-visual">
@@ -42,31 +45,14 @@
     <script src="../../animal.js"></script>
     <script>
         function runDemo() {
-            // Note: Animal.js might handle CSS vars via standard style property setting.
-            // WAAPI supports CSS variables in some browsers if registered, 
-            // but standard JS fallback usually handles it if implemented.
-            // Let's see if simple style setting works or if we need a custom updater.
-            
-            // NOTE: animal.js 'animate' logic mainly targets keys in ALIASES or transforms.
-            // Custom keys like '--box-width' might fall into 'jsProps'.
-            // The JS loop: el.style[k] = val.
-            // For CSS vars, one must use el.style.setProperty(k, val).
-            // animal.js: "else if (k in el.style) { el.style[k] = val; }"
-            // CSS vars are NOT "in el.style" directly as properties usually (they are via setProperty).
-            // So animal.js might FAIL to animate CSS vars without modification.
-            
-            // Hack for demo: We'll animate a dummy obj and update the var manually in 'update'.
-            
+            const $ = animal;
             const box = document.querySelector('.box');
             box.style.setProperty('--box-width', '50px');
-            
-            const proxy = { width: 50 };
-            animal.animate(proxy, {
-                width: 200,
+
+            $('.box').animate({
+                '--box-width': '200px',
                 duration: 1000,
-                update: () => {
-                    box.style.setProperty('--box-width', proxy.width + 'px');
-                }
+                easing: 'ease-in-out'
             });
         }
         setTimeout(runDemo, 500);

+ 3 - 3
doc/animatable_properties/test_js_props.html

@@ -21,11 +21,11 @@
                 <div class="tabs"><div class="tab active">JavaScript</div></div>
             </div>
             <div id="js-code" class="code-view active">
+<span class="kwd">const</span> $ <span class="punc">=</span> <span class="fun">animal</span><span class="punc">;</span>
 <span class="kwd">const</span> <span class="val">obj</span> <span class="punc">=</span> <span class="punc">{</span> score<span class="punc">:</span> <span class="num">0</span> <span class="punc">}</span><span class="punc">;</span>
-<span class="fun">animate</span><span class="punc">(</span><span class="val">obj</span><span class="punc">,</span> <span class="punc">{</span>
+<span class="fun">$</span><span class="punc">(</span><span class="val">obj</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">(</span><span class="punc">{</span>
   score<span class="punc">:</span> <span class="num">1000</span><span class="punc">,</span>
-  round<span class="punc">:</span> <span class="num">1</span><span class="punc">,</span>
-  update<span class="punc">:</span> <span class="punc">()</span> <span class="punc">=></span> updateUI(obj.score)
+  update<span class="punc">:</span> <span class="punc">()</span> <span class="punc">=></span> updateUI(Math.round(obj.score))
 <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
             </div>
             <div class="demo-visual">

+ 5 - 2
doc/animatable_properties/test_transforms.html

@@ -26,7 +26,9 @@
                 <div class="tabs"><div class="tab active">JavaScript</div></div>
             </div>
             <div id="js-code" class="code-view active">
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'.box'</span><span class="punc">,</span> <span class="punc">{</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
+
+<span class="fun">$</span><span class="punc">(</span><span class="str">'.box'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span>
   translateX<span class="punc">:</span> <span class="num">100</span><span class="punc">,</span>
   rotate<span class="punc">:</span> <span class="str">'1turn'</span><span class="punc">,</span>
   scale<span class="punc">:</span> <span class="num">0.5</span>
@@ -42,7 +44,8 @@
     <script>
         function runDemo() {
             document.querySelector('.box').style.transform = 'none';
-            animal.animate('.box', { translateX: 100, rotate: '1turn', scale: 0.5, duration: 1000 });
+            const $ = animal;
+            $('.box').animate({ translateX: 100, rotate: '1turn', scale: 0.5, duration: 1000 });
         }
         setTimeout(runDemo, 500);
     </script>

+ 148 - 4
doc/demo.css

@@ -7,6 +7,12 @@
     --code-bg: #000;
     --orange: #FF8F42;
     --border-color: #222;
+    --panel-bg: #171717;
+    --panel-bg-2: #141414;
+    --muted-1: #666;
+    --muted-2: #888;
+    --text-strong: #e6e6e6;
+    --shadow-1: 0 10px 40px rgba(0,0,0,0.25);
 }
 
 body {
@@ -14,14 +20,14 @@ body {
     color: var(--text-color);
     font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
     margin: 0;
-    padding: 40px;
+    padding: 60px 60px;
     display: flex;
     justify-content: center;
     overflow-y: auto;
 }
 
 .container {
-    max-width: 900px;
+    max-width: 980px;
     width: 100%;
 }
 
@@ -62,13 +68,37 @@ code.inline {
     font-size: 0.9em;
 }
 
+/* Page top meta (breadcrumb + since) */
+.page-top {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-bottom: 18px;
+}
+
+.crumb {
+    font-size: 10px;
+    font-weight: 800;
+    letter-spacing: 1px;
+    color: var(--highlight-color);
+    text-transform: uppercase;
+}
+
+.since {
+    font-size: 10px;
+    font-weight: 700;
+    letter-spacing: 1px;
+    color: #7a6a55;
+    text-transform: uppercase;
+}
+
 /* Code & Demo Box */
 .box-container {
     margin-top: 50px;
-    background: #171717;
+    background: var(--panel-bg);
     border-radius: 6px;
     overflow: hidden;
-    box-shadow: 0 10px 40px rgba(0,0,0,0.2);
+    box-shadow: var(--shadow-1);
 }
 
 .box-header {
@@ -87,6 +117,13 @@ code.inline {
     font-size: 14px;
 }
 
+.box-right {
+    display: flex;
+    align-items: center;
+    gap: 14px;
+    height: 100%;
+}
+
 .tabs {
     display: flex;
     gap: 20px;
@@ -143,6 +180,38 @@ code.inline {
 .punc { color: #abb2bf; }
 .com { color: #5c6370; font-style: italic; }
 
+/* Header icon buttons (copy, etc.) */
+.icon-btn {
+    appearance: none;
+    border: 1px solid transparent;
+    background: transparent;
+    color: #777;
+    width: 28px;
+    height: 28px;
+    border-radius: 8px;
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    transition: border-color 0.15s, background 0.15s, color 0.15s;
+}
+
+.icon-btn:hover {
+    color: #b0b0b0;
+    border-color: #2a2a2a;
+    background: rgba(255,255,255,0.03);
+}
+
+.icon-btn:active {
+    transform: translateY(1px);
+}
+
+.icon-btn svg {
+    width: 16px;
+    height: 16px;
+    display: block;
+}
+
 /* Visual Demo Area */
 .demo-visual {
     padding: 30px;
@@ -178,3 +247,78 @@ code.inline {
     border-color: var(--orange);
     color: var(--orange);
 }
+
+/* Toast */
+.toast {
+    position: fixed;
+    right: 16px;
+    bottom: 16px;
+    background: rgba(20,20,20,0.95);
+    border: 1px solid #2a2a2a;
+    color: #ddd;
+    padding: 10px 12px;
+    border-radius: 10px;
+    font-size: 12px;
+    box-shadow: 0 12px 30px rgba(0,0,0,0.35);
+    opacity: 0;
+    transform: translateY(10px);
+    transition: opacity 0.18s, transform 0.18s;
+    pointer-events: none;
+}
+
+.toast.show {
+    opacity: 1;
+    transform: translateY(0);
+}
+
+/* Previous / Next nav */
+.doc-nav {
+    margin-top: 28px;
+    padding-top: 18px;
+    border-top: 1px solid var(--border-color);
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #aaa;
+    font-size: 12px;
+}
+
+.doc-nav a {
+    color: #aaa;
+    text-decoration: none;
+    padding: 10px 12px;
+    border-radius: 10px;
+    border: 1px solid transparent;
+    min-width: 180px;
+    display: inline-flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: 14px;
+    background: rgba(255,255,255,0.02);
+}
+
+.doc-nav a:hover {
+    border-color: #2a2a2a;
+    background: rgba(255,255,255,0.03);
+    color: #ddd;
+}
+
+.doc-nav .nav-label {
+    color: #777;
+    font-weight: 700;
+    text-transform: uppercase;
+    letter-spacing: 0.8px;
+    font-size: 10px;
+}
+
+.doc-nav .nav-title {
+    color: #cfcfcf;
+    font-weight: 600;
+    font-size: 12px;
+}
+
+.doc-nav .nav-center {
+    color: #7a7a7a;
+    font-weight: 600;
+    letter-spacing: 0.4px;
+}

+ 168 - 0
doc/examples/controls.html

@@ -0,0 +1,168 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Controls (chain / seek / scroll) - Animal.js</title>
+    <link rel="stylesheet" href="../demo.css">
+    <style>
+        .demo-visual {
+            gap: 18px;
+        }
+        .row {
+            width: 100%;
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            gap: 14px;
+        }
+        .box {
+            width: 48px;
+            height: 48px;
+            border-radius: 10px;
+            background: var(--highlight-color);
+            transform: translateX(0px);
+        }
+        .bar-wrap {
+            width: 100%;
+            background: rgba(255,255,255,0.05);
+            border: 1px solid rgba(255,255,255,0.08);
+            border-radius: 999px;
+            overflow: hidden;
+            height: 10px;
+        }
+        .bar {
+            height: 100%;
+            width: 0%;
+            background: var(--orange);
+        }
+        .hint {
+            font-size: 12px;
+            color: #888;
+        }
+        .spacer {
+            height: 80vh; /* enough to scroll inside the iframe */
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <div class="page-top">
+            <div class="crumb">ANIMATION &gt; EXAMPLES</div>
+            <div class="since">CONTROLS</div>
+        </div>
+
+        <h1>Controls</h1>
+        <p class="description">
+            <code class="inline">animate()</code> returns a <strong>thenable controls object</strong>.
+            You can <code class="inline">play/pause</code>, <code class="inline">seek()</code> (0..1),
+            and wire it to <code class="inline">scroll()</code>.
+        </p>
+
+        <div class="box-container">
+            <div class="box-header">
+                <div class="box-title">Controls example</div>
+                <div class="tabs">
+                    <div class="tab active" onclick="switchTab('js')">JavaScript</div>
+                    <div class="tab" onclick="switchTab('html')">HTML</div>
+                </div>
+            </div>
+
+            <pre id="js-code" class="code-view active">const $ = animal;
+
+const ctl = $('.ex-box').animate({
+  x: 220,
+  rotate: 180,
+  opacity: 0.6,
+  duration: 800,
+  autoplay: false
+});
+
+// chainable controls
+ctl.seek(0.25).play();
+
+// still awaitable / thenable
+ctl.then(() =&gt; console.log('done'));
+
+// scroll-linked (works with WAAPI + JS fallback)
+const progress = $('.ex-bar').animate({
+  width: ['0%', '100%'],
+  autoplay: false,
+  duration: 1000
+});
+$.scroll(progress);</pre>
+
+            <pre id="html-code" class="html-view">&lt;div class="ex-box"&gt;&lt;/div&gt;
+&lt;div class="bar-wrap"&gt;
+  &lt;div class="ex-bar"&gt;&lt;/div&gt;
+&lt;/div&gt;</pre>
+
+            <div class="demo-visual">
+                <div class="row">
+                    <div class="box ex-box" aria-label="demo box"></div>
+                    <div class="hint">Click REPLAY, then scroll inside this panel.</div>
+                </div>
+                <div class="bar-wrap">
+                    <div class="bar ex-bar" aria-label="progress bar"></div>
+                </div>
+                <div class="spacer"></div>
+                <div class="hint">Scroll target area (spacer)</div>
+            </div>
+
+            <div class="action-bar">
+                <button class="play-btn" onclick="runDemo()">REPLAY</button>
+            </div>
+        </div>
+    </div>
+
+    <script src="../../animal.js"></script>
+    <script>
+        function switchTab(tab) {
+            document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
+            document.querySelectorAll('.code-view, .html-view').forEach(v => v.classList.remove('active'));
+            if (tab === 'js') {
+                document.querySelector('.tabs .tab:nth-child(1)').classList.add('active');
+                document.getElementById('js-code').classList.add('active');
+            } else {
+                document.querySelector('.tabs .tab:nth-child(2)').classList.add('active');
+                document.getElementById('html-code').classList.add('active');
+            }
+        }
+
+        let cleanup = null;
+        function runDemo() {
+            const $ = animal;
+            // reset
+            const box = document.querySelector('.ex-box');
+            const bar = document.querySelector('.ex-bar');
+            box.style.transform = 'none';
+            box.style.opacity = '1';
+            bar.style.width = '0%';
+
+            // Remove previous scroll listener if any
+            if (cleanup) cleanup();
+            cleanup = null;
+
+            const ctl = $('.ex-box').animate({
+                x: 220,
+                rotate: 180,
+                opacity: 0.6,
+                duration: 800,
+                autoplay: false
+            });
+            ctl.seek(0.25).play();
+
+            const progress = $('.ex-bar').animate({
+                width: ['0%', '100%'],
+                autoplay: false,
+                duration: 1000
+            });
+            cleanup = $.scroll(progress);
+        }
+
+        setTimeout(runDemo, 300);
+    </script>
+</body>
+</html>
+
+

+ 183 - 0
doc/examples/layer.html

@@ -0,0 +1,183 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Layer (popup / bind click) - Animal.js</title>
+  <link rel="stylesheet" href="../demo.css">
+  <style>
+    .demo-visual {
+      gap: 14px;
+      align-items: stretch;
+    }
+    .row {
+      display: flex;
+      gap: 12px;
+      flex-wrap: wrap;
+      align-items: center;
+    }
+    .btn {
+      appearance: none;
+      border: 1px solid rgba(255,255,255,0.12);
+      background: rgba(255,255,255,0.04);
+      color: #fff;
+      border-radius: 999px;
+      padding: 10px 14px;
+      font-weight: 650;
+      cursor: pointer;
+      transition: border-color 0.15s ease, background 0.15s ease;
+    }
+    .btn:hover {
+      border-color: rgba(255,255,255,0.22);
+      background: rgba(255,255,255,0.06);
+    }
+    .btn.danger {
+      border-color: rgba(255,75,75,0.5);
+      background: rgba(255,75,75,0.12);
+    }
+    .btn.danger:hover {
+      border-color: rgba(255,75,75,0.7);
+      background: rgba(255,75,75,0.18);
+    }
+    .hint {
+      font-size: 12px;
+      color: #8c8c8c;
+      line-height: 1.5;
+    }
+    .pill {
+      display: inline-flex;
+      align-items: center;
+      gap: 8px;
+      border: 1px solid rgba(255,255,255,0.08);
+      background: rgba(255,255,255,0.03);
+      border-radius: 999px;
+      padding: 8px 12px;
+      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+      font-size: 12px;
+      color: #b7b7b7;
+      overflow-x: auto;
+      max-width: 100%;
+      white-space: nowrap;
+    }
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="page-top">
+      <div class="crumb">EXAMPLES</div>
+      <div class="since">LAYER</div>
+    </div>
+
+    <h1>Layer</h1>
+    <p class="description">
+      <code class="inline">layer.js</code> exposes a global <code class="inline">Layer</code>.
+      <code class="inline">animal.js</code> adds <code class="inline">Selection.layer()</code> to bind click-to-popup.
+    </p>
+
+    <div class="box-container">
+      <div class="box-header">
+        <div class="box-title">Layer example</div>
+        <div class="tabs">
+          <div class="tab active" onclick="switchTab('js')">JavaScript</div>
+          <div class="tab" onclick="switchTab('html')">HTML</div>
+        </div>
+      </div>
+
+      <pre id="js-code" class="code-view active">const $ = animal;
+
+// 1) Bind click popup (recommended for buttons / links)
+$('.btn-delete').layer({
+  title: 'Delete item?',
+  text: 'This action cannot be undone.',
+  icon: 'warning',
+  showCancelButton: true
+});
+
+// 2) Bind from data-* (no JS options needed)
+$('.btn-data').layer(); // reads data-layer-* attributes
+
+// 3) Fire directly (manual usage)
+document.querySelector('.btn-direct').addEventListener('click', () =&gt; {
+  $.layer({ title: 'Hello from $.layer()', icon: 'success' });
+});</pre>
+
+      <pre id="html-code" class="html-view">&lt;button class="btn danger btn-delete"&gt;Delete (bind .layer)&lt;/button&gt;
+
+&lt;button
+  class="btn btn-data"
+  data-layer-title="Data-driven popup"
+  data-layer-text="Configured via data-layer-* attributes"
+  data-layer-icon="success"
+&gt;Open (data-layer-*)&lt;/button&gt;
+
+&lt;button class="btn btn-direct"&gt;Open ($.layer)&lt;/button&gt;</pre>
+
+      <div class="demo-visual">
+        <div class="row">
+          <button class="btn danger btn-delete">Delete (bind .layer)</button>
+
+          <button
+            class="btn btn-data"
+            data-layer-title="Data-driven popup"
+            data-layer-text="Configured via data-layer-* attributes"
+            data-layer-icon="success"
+          >Open (data-layer-*)</button>
+
+          <button class="btn btn-direct">Open ($.layer)</button>
+        </div>
+        <div class="hint">
+          Make sure you included both <code class="inline">animal.js</code> and <code class="inline">layer.js</code>.
+          For click-binding, <span class="pill">$('.btn').layer(options)</span> will de-dupe old handlers if called again.
+        </div>
+      </div>
+
+      <div class="action-bar">
+        <button class="play-btn" onclick="runDemo()">RE-BIND</button>
+      </div>
+    </div>
+  </div>
+
+  <script src="../../animal.js"></script>
+  <script src="../../layer.js"></script>
+  <script>
+    function switchTab(tab) {
+      document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
+      document.querySelectorAll('.code-view, .html-view').forEach(v => v.classList.remove('active'));
+      if (tab === 'js') {
+        document.querySelector('.tabs .tab:nth-child(1)').classList.add('active');
+        document.getElementById('js-code').classList.add('active');
+      } else {
+        document.querySelector('.tabs .tab:nth-child(2)').classList.add('active');
+        document.getElementById('html-code').classList.add('active');
+      }
+    }
+
+    function runDemo() {
+      const $ = animal;
+
+      // bind 1) explicit options
+      $('.btn-delete').layer({
+        title: 'Delete item?',
+        text: 'This action cannot be undone.',
+        icon: 'warning',
+        showCancelButton: true
+      });
+
+      // bind 2) data-* driven
+      $('.btn-data').layer();
+
+      // 3) direct fire
+      const direct = document.querySelector('.btn-direct');
+      if (direct) {
+        direct.onclick = () => {
+          $.layer({ title: 'Hello from $.layer()', text: 'Direct call (alias of $.fire)', icon: 'success' });
+        };
+      }
+    }
+
+    setTimeout(runDemo, 300);
+  </script>
+</body>
+</html>
+
+

+ 110 - 0
doc/examples/list.html

@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Examples - List</title>
+    <link rel="stylesheet" href="../list.css">
+    <style>
+        .card {
+            min-height: 120px;
+            padding: 18px 18px 16px 18px;
+        }
+        .card-preview {
+            height: 62px;
+            border-radius: 10px;
+            background: rgba(255,255,255,0.02);
+            border: 1px solid rgba(255,255,255,0.04);
+            position: relative;
+            overflow: hidden;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            margin-bottom: 14px;
+        }
+        .preview-pill {
+            font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+            color: #9a9a9a;
+            background: rgba(255,255,255,0.04);
+            border: 1px solid rgba(255,255,255,0.06);
+            padding: 10px 14px;
+            border-radius: 10px;
+            font-size: 13px;
+            letter-spacing: 0.2px;
+        }
+    </style>
+</head>
+<body>
+    <div class="search-header">
+        <div class="search-title">Examples</div>
+        <div class="search-input-container">
+            <div class="search-icon"></div>
+            <input type="text" class="search-input" placeholder="Filter examples..." disabled>
+        </div>
+    </div>
+
+    <div class="grid-section">
+        <div class="header-card active" onclick="selectItem('controls.html', this)">
+            <div class="header-dots"></div>
+            <div class="header-title">Examples</div>
+        </div>
+
+        <div class="card-grid" style="margin-top: 40px;">
+            <div class="card active" data-content="examples/controls.html" onclick="selectItem('controls.html', this)">
+                <div class="card-preview">
+                    <div class="preview-pill">controls.seek(0.5).play()</div>
+                </div>
+                <div class="card-footer">Controls (chain / seek / scroll)</div>
+            </div>
+
+            <div class="card" data-content="examples/layer.html" onclick="selectItem('layer.html', this)">
+                <div class="card-preview">
+                    <div class="preview-pill">$('.btn').layer({ title: 'Hi' })</div>
+                </div>
+                <div class="card-footer">Layer (popup / bind click)</div>
+            </div>
+        </div>
+    </div>
+
+    <script>
+        function selectItem(url, el) {
+            document.querySelectorAll('.card').forEach(c => c.classList.remove('active'));
+            document.querySelectorAll('.header-card').forEach(c => c.classList.remove('active'));
+            el.classList.add('active');
+            if (window.parent && window.parent.loadContent) {
+                window.parent.loadContent('examples/' + url);
+            }
+        }
+
+        // Called from parent to keep active selection in sync with the right iframe
+        function setActiveByContentUrl(contentUrl) {
+            const normalized = String(contentUrl || '').replace(/^\.\//, '');
+            const header = document.querySelector('.header-card');
+            const cards = Array.from(document.querySelectorAll('.card[data-content]'));
+
+            document.querySelectorAll('.card').forEach(c => c.classList.remove('active'));
+            document.querySelectorAll('.header-card').forEach(c => c.classList.remove('active'));
+
+            if (!normalized) return;
+
+            if (normalized.includes('examples/controls.html')) {
+                header?.classList.add('active');
+                const c = cards.find(x => (x.dataset.content || '').includes('examples/controls.html'));
+                if (c) c.classList.add('active');
+                return;
+            }
+
+            if (normalized.includes('examples/layer.html')) {
+                header?.classList.add('active');
+                const c = cards.find(x => (x.dataset.content || '').includes('examples/layer.html'));
+                if (c) c.classList.add('active');
+                return;
+            }
+        }
+
+        window.setActiveByContentUrl = setActiveByContentUrl;
+    </script>
+</body>
+</html>
+
+

+ 40 - 0
doc/index.html

@@ -222,6 +222,9 @@
                     <!-- Callbacks Nested? Or separate? Reference shows Callbacks under Animation -->
                     <div class="nav-item" onclick="selectCategory('list_callbacks.html', 'test_on_update.html', this)">Callbacks</div>
                     
+                    <!-- Examples -->
+                    <div class="nav-item" onclick="selectCategory('examples/list.html', 'examples/controls.html', this)">Examples</div>
+                    
                     <div class="nav-item">Methods</div>
                     <div class="nav-item">Properties</div>
                 </div>
@@ -287,7 +290,44 @@
             if (contentFrame.getAttribute('src') !== url) {
                 contentFrame.src = url;
             }
+
+            // Keep middle list selection in sync when possible
+            try {
+                setMiddleActive(url);
+            } catch {}
         }
+
+        // Called by content iframe pages (and also internally after navigation)
+        function setMiddleActive(contentUrl) {
+            const middleFrame = document.getElementById('middle-frame');
+            if (!middleFrame) return;
+            const win = middleFrame.contentWindow;
+            if (win && typeof win.setActiveByContentUrl === 'function') {
+                win.setActiveByContentUrl(contentUrl);
+            }
+        }
+
+        // Make initial active state robust against iframe load ordering
+        window.addEventListener('load', () => {
+            const contentFrame = document.getElementById('content-frame');
+            const src = contentFrame?.getAttribute('src');
+            if (src) setMiddleActive(src);
+
+            const middleFrame = document.getElementById('middle-frame');
+            if (middleFrame) {
+                middleFrame.addEventListener('load', () => {
+                    const current = document.getElementById('content-frame')?.getAttribute('src');
+                    if (current) setMiddleActive(current);
+                });
+            }
+
+            if (contentFrame) {
+                contentFrame.addEventListener('load', () => {
+                    const current = contentFrame.getAttribute('src');
+                    if (current) setMiddleActive(current);
+                });
+            }
+        });
     </script>
 </body>
 </html>

+ 7 - 13
doc/list_callbacks.html

@@ -16,30 +16,24 @@
     </div>
 
     <div class="grid-section">
-        <div class="section-title">Standard Callbacks</div>
+        <div class="section-title">Callbacks</div>
         <div class="card-grid">
-            <!-- onUpdate -->
+            <!-- update -->
             <div class="card active" onclick="selectItem('test_on_update.html', this)">
                 <div class="card-large-icon">fn</div>
-                <div class="card-footer">onUpdate</div>
+                <div class="card-footer">update</div>
             </div>
             
-            <!-- onBegin -->
+            <!-- begin -->
             <div class="card" onclick="selectItem('test_on_update.html', this)">
                 <div class="card-large-icon">fn</div>
-                <div class="card-footer">onBegin</div>
+                <div class="card-footer">begin</div>
             </div>
 
-            <!-- onComplete -->
+            <!-- complete -->
             <div class="card" onclick="selectItem('test_on_update.html', this)">
                 <div class="card-large-icon">fn</div>
-                <div class="card-footer">onComplete</div>
-            </div>
-            
-            <!-- onLoop -->
-            <div class="card" onclick="selectItem('test_on_update.html', this)">
-                <div class="card-large-icon">fn</div>
-                <div class="card-footer">onLoop</div>
+                <div class="card-footer">complete</div>
             </div>
         </div>
     </div>

+ 2 - 1
doc/search.html

@@ -71,6 +71,7 @@
     
     <script src="../animal.js"></script>
     <script>
+        const $ = animal;
         // --- Rotation Logic ---
         let jsRotations = 0;
         let reactRotations = 0;
@@ -89,7 +90,7 @@
             let current = (iconId === 'js-icon') ? ++jsRotations : ++reactRotations;
             
             // Use animal.js to rotate
-            animal.animate(icon, {
+            $(icon).animate({
                 rotate: current * 360, 
                 duration: 600,
                 easing: 'ease-out'

+ 121 - 16
doc/targets/list.html

@@ -5,6 +5,70 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Targets - List</title>
     <link rel="stylesheet" href="../list.css">
+    <style>
+        /* Targets-specific card previews (to match anime.js docs vibe) */
+        .card {
+            min-height: 120px;
+            padding: 18px 18px 16px 18px;
+        }
+
+        .card-preview {
+            height: 62px;
+            border-radius: 10px;
+            background: rgba(255,255,255,0.02);
+            border: 1px solid rgba(255,255,255,0.04);
+            position: relative;
+            overflow: hidden;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            margin-bottom: 14px;
+        }
+
+        .preview-grid {
+            width: 100%;
+            padding: 0 14px;
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+        }
+
+        .preview-stack {
+            display: flex;
+            flex-direction: column;
+            gap: 6px;
+        }
+
+        .preview-sq {
+            width: 12px;
+            height: 12px;
+            border-radius: 3px;
+            background: #3a3a3a;
+            transition: transform 0.35s ease, background 0.2s ease;
+        }
+
+        .preview-sq.orange { background: #ff9f43; }
+        .preview-sq.red { background: #ff4b4b; }
+
+        /* Hover gives a subtle “replay” feel like anime.js cards */
+        .card:hover .preview-stack.left .preview-sq {
+            transform: translateX(10px);
+        }
+        .card:hover .preview-stack.right .preview-sq {
+            transform: translateX(-10px);
+        }
+
+        .preview-pill {
+            font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+            color: #9a9a9a;
+            background: rgba(255,255,255,0.04);
+            border: 1px solid rgba(255,255,255,0.06);
+            padding: 10px 14px;
+            border-radius: 10px;
+            font-size: 13px;
+            letter-spacing: 0.2px;
+        }
+    </style>
 </head>
 <body>
     <div class="search-header">
@@ -25,38 +89,56 @@
 
         <div class="card-grid" style="margin-top: 40px;">
             <!-- CSS Selector -->
-            <div class="card" onclick="selectItem('test_css_selector.html', this)">
-                <div class="card-large-icon">
-                    <div class="icon-stack">
-                        <div class="icon-square"></div>
-                        <div class="icon-square"></div>
-                        <div class="icon-square"></div>
+            <div class="card" data-content="targets/test_css_selector.html" onclick="selectItem('test_css_selector.html', this)">
+                <div class="card-preview">
+                    <div class="preview-grid">
+                        <div class="preview-stack left" aria-hidden="true">
+                            <div class="preview-sq"></div>
+                            <div class="preview-sq"></div>
+                            <div class="preview-sq"></div>
+                        </div>
+                        <div class="preview-stack right" aria-hidden="true">
+                            <div class="preview-sq orange"></div>
+                            <div class="preview-sq orange"></div>
+                            <div class="preview-sq orange"></div>
+                        </div>
                     </div>
                 </div>
                 <div class="card-footer">CSS Selector</div>
             </div>
             
             <!-- DOM Elements -->
-            <div class="card" onclick="selectItem('test_dom_elements.html', this)">
-                <div class="card-large-icon">
-                    <div class="icon-stack">
-                        <div class="icon-square"></div>
-                        <div class="icon-square"></div>
-                        <div class="icon-square"></div>
+            <div class="card" data-content="targets/test_dom_elements.html" onclick="selectItem('test_dom_elements.html', this)">
+                <div class="card-preview">
+                    <div class="preview-grid">
+                        <div class="preview-stack left" aria-hidden="true">
+                            <div class="preview-sq"></div>
+                            <div class="preview-sq"></div>
+                            <div class="preview-sq"></div>
+                        </div>
+                        <div class="preview-stack right" aria-hidden="true">
+                            <div class="preview-sq red"></div>
+                            <div class="preview-sq"></div>
+                            <div class="preview-sq"></div>
+                        </div>
                     </div>
                 </div>
                 <div class="card-footer">DOM Elements</div>
             </div>
 
             <!-- JS Objects -->
-            <div class="card" onclick="selectItem('test_js_objects.html', this)">
-                <div class="card-large-icon" style="color: #666; font-size: 14px;">{}</div> <!-- Keep text icon but style it -->
+            <div class="card" data-content="targets/test_js_objects.html" onclick="selectItem('test_js_objects.html', this)">
+                <div class="card-preview">
+                    <div class="preview-pill">{ "x": 0, "y": 0 }</div>
+                </div>
                 <div class="card-footer">JavaScript Objects <span class="tag">JS</span></div>
             </div>
             
             <!-- Arrays -->
-            <div class="card" onclick="selectItem('test_array_targets.html', this)">
-                <div class="card-large-icon" style="color: #666; font-size: 14px;">[]</div>
+            <div class="card" data-content="targets/test_array_targets.html" onclick="selectItem('test_array_targets.html', this)">
+                <div class="card-preview">
+                    <div class="preview-pill">{ "x": "0" }</div>
+                </div>
                 <div class="card-footer">Array of targets</div>
             </div>
         </div>
@@ -78,6 +160,29 @@
                 window.parent.loadContent('targets/' + url);
             }
         }
+
+        // Called from parent to keep active selection in sync with the right iframe
+        function setActiveByContentUrl(contentUrl) {
+            const normalized = String(contentUrl || '').replace(/^\.\//, '');
+            const header = document.querySelector('.header-card');
+            const cards = Array.from(document.querySelectorAll('.card[data-content]'));
+
+            document.querySelectorAll('.card').forEach(c => c.classList.remove('active'));
+            document.querySelectorAll('.header-card').forEach(c => c.classList.remove('active'));
+
+            if (!normalized) return;
+
+            if (normalized.includes('targets/overview.html')) {
+                header?.classList.add('active');
+                return;
+            }
+
+            const match = cards.find(c => normalized.endsWith(c.dataset.content) || normalized.includes(c.dataset.content));
+            if (match) match.classList.add('active');
+        }
+
+        // expose for parent iframe access
+        window.setActiveByContentUrl = setActiveByContentUrl;
     </script>
 </body>
 </html>

+ 8 - 8
doc/targets/overview.html

@@ -43,21 +43,21 @@
 
         <div class="box-container" style="margin-top: 30px;">
             <div class="code-view active" style="background: #161616; padding: 40px;">
-                <span class="fun">animate</span>(<br>
-                &nbsp;&nbsp;<span class="fun">$</span>(<span class="str">'.square'</span>)<span class="annotation">Targets</span>,<br>
+                <span class="kwd">const</span> $ <span class="punc">=</span> <span class="fun">animal</span>;<br><br>
+                <span class="fun">$</span>(<span class="str">'.square'</span>)<span class="annotation">Targets</span>.<span class="fun">animate</span>(<br>
                 &nbsp;&nbsp;{<br>
-                &nbsp;&nbsp;&nbsp;&nbsp;translateX: <span class="num">100</span>,<br>
+                &nbsp;&nbsp;&nbsp;&nbsp;x: <span class="num">100</span>,<br>
                 &nbsp;&nbsp;&nbsp;&nbsp;scale: <span class="num">2</span>,<br>
                 &nbsp;&nbsp;&nbsp;&nbsp;opacity: <span class="num">.5</span>,<br>
                 &nbsp;&nbsp;&nbsp;&nbsp;duration: <span class="num">400</span>,<br>
                 &nbsp;&nbsp;&nbsp;&nbsp;delay: <span class="num">250</span>,<br>
-                &nbsp;&nbsp;&nbsp;&nbsp;ease: <span class="str">'out(3)'</span>,<br>
+                &nbsp;&nbsp;&nbsp;&nbsp;easing: <span class="str">'ease-out'</span>,<br>
                 &nbsp;&nbsp;&nbsp;&nbsp;loop: <span class="num">3</span>,<br>
-                &nbsp;&nbsp;&nbsp;&nbsp;alternate: <span class="kwd">true</span>,<br>
+                &nbsp;&nbsp;&nbsp;&nbsp;direction: <span class="str">'alternate'</span>,<br>
                 &nbsp;&nbsp;&nbsp;&nbsp;autoplay: <span class="kwd">false</span>,<br>
-                &nbsp;&nbsp;&nbsp;&nbsp;<span class="fun">onBegin</span>: () => {},<br>
-                &nbsp;&nbsp;&nbsp;&nbsp;<span class="fun">onLoop</span>: () => {},<br>
-                &nbsp;&nbsp;&nbsp;&nbsp;<span class="fun">onUpdate</span>: () => {},<br>
+                &nbsp;&nbsp;&nbsp;&nbsp;<span class="fun">begin</span>: () => {},<br>
+                &nbsp;&nbsp;&nbsp;&nbsp;<span class="fun">update</span>: () => {},<br>
+                &nbsp;&nbsp;&nbsp;&nbsp;<span class="fun">complete</span>: () => {},<br>
                 &nbsp;&nbsp;}<br>
                 );
             </div>

+ 4 - 3
doc/targets/test_array_targets.html

@@ -39,12 +39,12 @@
 
             <!-- JS Code View -->
             <div id="js-code" class="code-view active">
-<span class="kwd">import</span> <span class="punc">{</span> animate <span class="punc">}</span> <span class="kwd">from</span> <span class="str">'animejs'</span><span class="punc">;</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
 
 <span class="kwd">const</span> <span class="val">el1</span> <span class="punc">=</span> document.querySelector<span class="punc">(</span><span class="str">'.el-1'</span><span class="punc">);</span>
 <span class="kwd">const</span> <span class="val">el2</span> <span class="punc">=</span> document.querySelector<span class="punc">(</span><span class="str">'.el-2'</span><span class="punc">);</span>
 
-<span class="fun">animate</span><span class="punc">(</span><span class="punc">[</span><span class="val">el1</span><span class="punc">,</span> <span class="val">el2</span><span class="punc">]</span><span class="punc">,</span> <span class="punc">{</span> x<span class="punc">:</span> <span class="num">200</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+<span class="fun">$</span><span class="punc">(</span><span class="punc">[</span><span class="val">el1</span><span class="punc">,</span> <span class="val">el2</span><span class="punc">]</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span> x<span class="punc">:</span> <span class="num">200</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
             </div>
 
             <!-- HTML Code View -->
@@ -87,10 +87,11 @@
                 el.style.transform = 'none';
             });
             
+            const $ = animal;
             const el1 = document.querySelector('.el-1');
             const el2 = document.querySelector('.el-2');
             
-            animal.animate([el1, el2], { 
+            $([el1, el2]).animate({ 
                 x: 200, 
                 duration: 1000,
                 easing: 'ease-out'

+ 161 - 24
doc/targets/test_css_selector.html

@@ -8,30 +8,63 @@
     <style>
         .row {
             position: relative;
-            padding: 20px 0;
-            border-bottom: 1px solid #2a2a2a;
+            padding: 18px 0;
+            border-bottom: 1px solid #222;
             display: flex;
             align-items: center;
+            min-height: 48px;
         }
         .row:last-child { border-bottom: none; }
+
+        .row::before {
+            content: '';
+            position: absolute;
+            left: 0;
+            right: 0;
+            top: 50%;
+            height: 1px;
+            background: #262626;
+            transform: translateY(-0.5px);
+        }
         
         .square {
-            width: 28px;
-            height: 28px;
+            position: relative;
+            z-index: 1;
+            width: 18px;
+            height: 18px;
             background-color: var(--highlight-color);
-            border-radius: 2px;
-            margin-right: 20px; /* Space if needed */
+            border-radius: 3px;
+            will-change: transform;
         }
+
+        .sq-lg { width: 28px; height: 28px; border-radius: 4px; }
+        .sq-md { width: 20px; height: 20px; border-radius: 3px; }
+        .sq-sm { width: 14px; height: 14px; border-radius: 3px; }
+
         #css-selector-id {
             background-color: var(--accent-color);
         }
+
+        .box-container { margin-top: 40px; }
+        .demo-visual { padding: 26px 30px; background: #151515; }
+
+        .demo-visual .row { margin: 0; }
+
+        .hint {
+            color: #888;
+            font-size: 13px;
+            margin-top: -8px;
+            margin-bottom: 18px;
+        }
     </style>
 </head>
 <body>
 
     <div class="container">
-        <!-- Breadcrumb / Header -->
-        <div style="font-size: 10px; font-weight: bold; letter-spacing: 1px; color: #ff4b4b; margin-bottom: 20px;">ANIMATION > TARGETS</div>
+        <div class="page-top">
+            <div class="crumb">ANIMATION › TARGETS</div>
+            <div class="since">SINCE 1.0.0</div>
+        </div>
         
         <h1>CSS Selector</h1>
         <p class="description">Targets one or multiple DOM Elements using a CSS selector.</p>
@@ -42,20 +75,28 @@
         <div class="box-container">
             <div class="box-header">
                 <div class="box-title">CSS Selector code example</div>
-                <div class="tabs">
-                    <div class="tab active" onclick="switchTab('js')">JavaScript</div>
-                    <div class="tab" onclick="switchTab('html')">HTML</div>
+                <div class="box-right">
+                    <button class="icon-btn" type="button" title="Copy" onclick="copyActiveCode()" aria-label="Copy code">
+                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
+                            <rect x="9" y="9" width="13" height="13" rx="2"></rect>
+                            <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
+                        </svg>
+                    </button>
+                    <div class="tabs" role="tablist" aria-label="Code tabs">
+                        <div class="tab active" role="tab" aria-selected="true" tabindex="0" onclick="switchTab('js')">JavaScript</div>
+                        <div class="tab" role="tab" aria-selected="false" tabindex="-1" onclick="switchTab('html')">HTML</div>
+                    </div>
                 </div>
             </div>
 
             <!-- JS Code View -->
             <div id="js-code" class="code-view active">
-<span class="kwd">import</span> <span class="punc">{</span> animate <span class="punc">}</span> <span class="kwd">from</span> <span class="str">'animejs'</span><span class="punc">;</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
 
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'.square'</span><span class="punc">,</span> <span class="punc">{</span> x<span class="punc">:</span> <span class="str">'17rem'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'#css-selector-id'</span><span class="punc">,</span> <span class="punc">{</span> rotate<span class="punc">:</span> <span class="str">'1turn'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'.row:nth-child(3) .square'</span><span class="punc">,</span> <span class="punc">{</span>
-  scale<span class="punc">:</span> <span class="punc">[</span><span class="num">1</span><span class="punc">,</span> <span class="num">.5</span><span class="punc">,</span> <span class="num">1</span><span class="punc">]</span>
+<span class="fun">$</span><span class="punc">(</span><span class="str">'.square'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span> x<span class="punc">:</span> <span class="str">'17rem'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+<span class="fun">$</span><span class="punc">(</span><span class="str">'#css-selector-id'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span> rotate<span class="punc">:</span> <span class="str">'1turn'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+<span class="fun">$</span><span class="punc">(</span><span class="str">'.row:nth-child(3) .square'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span>
+  scale<span class="punc">:</span> <span class="punc">[</span><span class="num">1</span><span class="punc">,</span> <span class="num">.5</span><span class="punc">]</span>
 <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
             </div>
 
@@ -76,15 +117,15 @@
             <div class="demo-visual">
                 <!-- Row 1 -->
                 <div class="medium row">
-                  <div class="square"></div>
+                  <div class="square sq-lg"></div>
                 </div>
                 <!-- Row 2 -->
                 <div class="medium row">
-                  <div id="css-selector-id" class="square"></div>
+                  <div id="css-selector-id" class="square sq-md"></div>
                 </div>
                 <!-- Row 3 -->
                 <div class="medium row">
-                  <div class="square"></div>
+                  <div class="square sq-sm"></div>
                 </div>
             </div>
             
@@ -93,8 +134,28 @@
             </div>
         </div>
 
+        <div class="doc-nav" aria-label="Previous and next navigation">
+            <a href="#" onclick="goPrev(); return false;">
+                <span>
+                    <span class="nav-label">Previous</span><br>
+                    <span class="nav-title">Targets</span>
+                </span>
+                <span aria-hidden="true">←</span>
+            </a>
+            <div class="nav-center">Targets</div>
+            <a href="#" onclick="goNext(); return false;">
+                <span>
+                    <span class="nav-label">Next</span><br>
+                    <span class="nav-title">DOM Elements</span>
+                </span>
+                <span aria-hidden="true">→</span>
+            </a>
+        </div>
+
     </div>
 
+    <div id="toast" class="toast" role="status" aria-live="polite"></div>
+
     <script src="../../animal.js"></script>
     <script>
         function switchTab(tab) {
@@ -104,9 +165,48 @@
             if (tab === 'js') {
                 document.querySelector('.tabs .tab:nth-child(1)').classList.add('active');
                 document.getElementById('js-code').classList.add('active');
+                document.querySelector('.tabs .tab:nth-child(1)').setAttribute('aria-selected', 'true');
+                document.querySelector('.tabs .tab:nth-child(2)').setAttribute('aria-selected', 'false');
+                document.querySelector('.tabs .tab:nth-child(1)').tabIndex = 0;
+                document.querySelector('.tabs .tab:nth-child(2)').tabIndex = -1;
             } else {
                 document.querySelector('.tabs .tab:nth-child(2)').classList.add('active');
                 document.getElementById('html-code').classList.add('active');
+                document.querySelector('.tabs .tab:nth-child(1)').setAttribute('aria-selected', 'false');
+                document.querySelector('.tabs .tab:nth-child(2)').setAttribute('aria-selected', 'true');
+                document.querySelector('.tabs .tab:nth-child(1)').tabIndex = -1;
+                document.querySelector('.tabs .tab:nth-child(2)').tabIndex = 0;
+            }
+        }
+
+        function showToast(msg) {
+            const el = document.getElementById('toast');
+            if (!el) return;
+            el.textContent = msg;
+            el.classList.add('show');
+            clearTimeout(el._t);
+            el._t = setTimeout(() => el.classList.remove('show'), 900);
+        }
+
+        async function copyActiveCode() {
+            const active = document.querySelector('.code-view.active, .html-view.active');
+            const text = active ? active.innerText.trimEnd() : '';
+            if (!text) return;
+
+            try {
+                await navigator.clipboard.writeText(text);
+                showToast('Copied');
+            } catch (e) {
+                // Fallback
+                const ta = document.createElement('textarea');
+                ta.value = text;
+                ta.style.position = 'fixed';
+                ta.style.opacity = '0';
+                document.body.appendChild(ta);
+                ta.select();
+                document.execCommand('copy');
+                ta.remove();
+                showToast('Copied');
             }
         }
 
@@ -115,24 +215,61 @@
                 el.style.transform = 'none';
             });
             
-            animal.animate('.square', { 
+            const $ = animal;
+
+            $('.square').animate({ 
                 x: '17rem', 
                 duration: 1000,
                 easing: 'ease-out'
             });
 
-            animal.animate('#css-selector-id', { 
+            $('#css-selector-id').animate({ 
                 rotate: '1turn', 
                 duration: 1000,
                 easing: 'ease-out'
             });
 
-            animal.animate('.medium.row:nth-child(3) .square', { 
-                scale: [1, .5, 1], 
-                duration: 1000,
+            $('.medium.row:nth-child(3) .square').animate({
+                scale: [1, .5],
+                duration: 500,
                 easing: 'ease-in-out'
+            }).then(() => {
+                $('.medium.row:nth-child(3) .square').animate({
+                    scale: [.5, 1],
+                    duration: 500,
+                    easing: 'ease-in-out'
+                });
             });
         }
+
+        function goPrev() {
+            if (window.parent && window.parent.loadContent) {
+                window.parent.loadContent('targets/overview.html');
+            }
+        }
+
+        function goNext() {
+            if (window.parent && window.parent.loadContent) {
+                window.parent.loadContent('targets/test_dom_elements.html');
+            }
+        }
+
+        // Keyboard support for tabs
+        document.addEventListener('keydown', (e) => {
+            const focused = document.activeElement;
+            if (!focused || !focused.classList.contains('tab')) return;
+            if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return;
+            e.preventDefault();
+            const isJs = focused.textContent.trim().toLowerCase().includes('javascript');
+            switchTab(isJs ? 'html' : 'js');
+            const next = document.querySelector('.tab.active');
+            next?.focus();
+        });
+
+        // Sync middle list selection when loaded inside doc/index.html
+        try {
+            window.parent?.setMiddleActive?.('targets/test_css_selector.html');
+        } catch {}
         
         setTimeout(runDemo, 500);
     </script>

+ 6 - 5
doc/targets/test_dom_elements.html

@@ -39,16 +39,16 @@
 
             <!-- JS Code View -->
             <div id="js-code" class="code-view active">
-<span class="kwd">import</span> <span class="punc">{</span> animate <span class="punc">}</span> <span class="kwd">from</span> <span class="str">'animejs'</span><span class="punc">;</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
 
 <span class="kwd">const</span> <span class="val">element</span> <span class="punc">=</span> document.querySelector<span class="punc">(</span><span class="str">'.dom-node'</span><span class="punc">);</span>
 <span class="kwd">const</span> <span class="val">elements</span> <span class="punc">=</span> document.querySelectorAll<span class="punc">(</span><span class="str">'.dom-node-list'</span><span class="punc">);</span>
 
-<span class="fun">animate</span><span class="punc">(</span><span class="val">element</span><span class="punc">,</span> <span class="punc">{</span>
+<span class="fun">$</span><span class="punc">(</span><span class="val">element</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span>
   x<span class="punc">:</span> <span class="num">200</span>
 <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
 
-<span class="fun">animate</span><span class="punc">(</span><span class="val">elements</span><span class="punc">,</span> <span class="punc">{</span>
+<span class="fun">$</span><span class="punc">(</span><span class="val">elements</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span>
   x<span class="punc">:</span> <span class="num">200</span>
 <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
             </div>
@@ -94,16 +94,17 @@
                 el.style.transform = 'none';
             });
             
+            const $ = animal;
             const element = document.querySelector('.dom-node');
             const elements = document.querySelectorAll('.dom-node-list');
             
-            animal.animate(element, { 
+            $(element).animate({ 
                 x: 200, 
                 duration: 1000,
                 easing: 'ease-out'
             });
 
-            animal.animate(elements, { 
+            $(elements).animate({ 
                 x: 200, 
                 duration: 1000,
                 delay: 200,

+ 4 - 3
doc/targets/test_js_objects.html

@@ -33,11 +33,11 @@
 
             <!-- JS Code View -->
             <div id="js-code" class="code-view active">
-<span class="kwd">import</span> <span class="punc">{</span> animate <span class="punc">}</span> <span class="kwd">from</span> <span class="str">'animejs'</span><span class="punc">;</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
 
 <span class="kwd">const</span> <span class="val">obj</span> <span class="punc">=</span> <span class="punc">{</span> prop<span class="punc">:</span> <span class="num">0</span> <span class="punc">}</span><span class="punc">;</span>
 
-<span class="fun">animate</span><span class="punc">(</span><span class="val">obj</span><span class="punc">,</span> <span class="punc">{</span>
+<span class="fun">$</span><span class="punc">(</span><span class="val">obj</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span>
   prop<span class="punc">:</span> <span class="num">100</span><span class="punc">,</span>
   update<span class="punc">:</span> <span class="punc">()</span> <span class="punc">=></span> console.log(obj.prop)
 <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
@@ -80,8 +80,9 @@
         function runDemo() {
             const logEl = document.getElementById('log-val');
             const obj = { prop: 0 };
+            const $ = animal;
             
-            animal.animate(obj, { 
+            $(obj).animate({ 
                 prop: 100, 
                 duration: 2000,
                 easing: 'linear',

+ 30 - 25
doc/test_on_update.html

@@ -3,7 +3,7 @@
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>onUpdate - Animal.js</title>
+    <title>Callbacks (update / begin / complete) - Animal.js</title>
     <link rel="stylesheet" href="demo.css">
     <style>
         .circle {
@@ -33,27 +33,34 @@
     <div class="container">
         <div style="font-size: 10px; font-weight: bold; letter-spacing: 1px; color: #ff4b4b; margin-bottom: 20px;">ANIMATION > CALLBACKS</div>
         
-        <h1>onUpdate</h1>
-        <p class="description">Executes a function on every frames of a running animation at the specified <code class="inline">frameRate</code>.</p>
+        <h1>Callbacks</h1>
+        <p class="description">
+            Pass <code class="inline">begin</code>, <code class="inline">update</code>, and <code class="inline">complete</code>
+            to <code class="inline">animate()</code>. The <code class="inline">update</code> callback receives
+            <code class="inline">{ target, progress, time }</code>.
+        </p>
 
         <h2>Accepts</h2>
-        <p>A <code class="inline">Function</code> whose first argument is the animation itself</p>
+        <p>A <code class="inline">Function</code>. For <code class="inline">update</code>, the first argument is an object: <code class="inline">{ target, progress, time }</code>.</p>
         
         <h2>Default</h2>
         <p><code class="inline">noop</code></p>
         
         <div style="background: rgba(33, 150, 243, 0.1); border-left: 4px solid #2196F3; padding: 15px; border-radius: 4px; margin-bottom: 30px; font-size: 14px;">
             <div style="font-weight: bold; color: #2196F3; margin-bottom: 5px; font-size: 10px; letter-spacing: 1px;">INFO</div>
-            <div style="color: #90caf9; margin-bottom: 10px;">To change the default value globally, update the <code class="inline" style="color: #fff">engine.defaults</code> object.</div>
+            <div style="color: #90caf9; margin-bottom: 10px;">Callbacks are configured per animation via the params object.</div>
             <div style="background: rgba(0,0,0,0.3); padding: 10px; border-radius: 4px; font-family: monospace; font-size: 12px; color: #a9b7c6; white-space: pre-wrap;">
-<span class="kwd">import</span> { engine } <span class="kwd">from</span> <span class="str">'animejs'</span>;
-engine.defaults.onUpdate = self => console.log(self.id);
+const $ = animal;
+$('.box').animate({
+  x: 200,
+  update: ({ progress }) => console.log(progress)
+});
             </div>
         </div>
 
         <div class="box-container">
             <div class="box-header">
-                <div class="box-title">onUpdate code example</div>
+                <div class="box-title">Callbacks code example</div>
                 <div class="tabs">
                     <div class="tab active" onclick="switchTab('js')">JavaScript</div>
                     <div class="tab" onclick="switchTab('html')">HTML</div>
@@ -62,19 +69,19 @@ engine.defaults.onUpdate = self => console.log(self.id);
 
             <!-- JS Code View -->
             <div id="js-code" class="code-view active">
-<span class="kwd">import</span> <span class="punc">{</span> animate<span class="punc">,</span> utils <span class="punc">}</span> <span class="kwd">from</span> <span class="str">'animejs'</span><span class="punc">;</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
 
-<span class="kwd">const</span> <span class="punc">[</span> $value <span class="punc">]</span> <span class="punc">=</span> utils.<span class="fun">$</span><span class="punc">(</span><span class="str">'.value'</span><span class="punc">);</span>
+<span class="kwd">const</span> $value <span class="punc">=</span> document.querySelector<span class="punc">(</span><span class="str">'.value'</span><span class="punc">);</span>
 
 <span class="kwd">let</span> updates <span class="punc">=</span> <span class="num">0</span><span class="punc">;</span>
 
-<span class="kwd">const</span> animation <span class="punc">=</span> <span class="fun">animate</span><span class="punc">(</span><span class="str">'.circle'</span><span class="punc">,</span> <span class="punc">{</span>
+<span class="fun">$</span><span class="punc">(</span><span class="str">'.circle'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">({</span>
   x<span class="punc">:</span> <span class="str">'16rem'</span><span class="punc">,</span>
-  loopDelay<span class="punc">:</span> <span class="num">1500</span><span class="punc">,</span>
-  loop<span class="punc">:</span> <span class="kwd">true</span><span class="punc">,</span>
-  alternate<span class="punc">:</span> <span class="kwd">true</span><span class="punc">,</span>
-  onUpdate<span class="punc">:</span> self <span class="punc">=></span> $value.textContent <span class="punc">=</span> ++updates
-<span class="punc">}</span><span class="punc">);</span>
+  loop<span class="punc">:</span> Infinity<span class="punc">,</span>
+  direction<span class="punc">:</span> <span class="str">'alternate'</span><span class="punc">,</span>
+  begin<span class="punc">:</span> <span class="punc">()</span> <span class="punc">=></span> $value.textContent <span class="punc">=</span> <span class="str">'0'</span><span class="punc">,</span>
+  update<span class="punc">:</span> <span class="punc">({</span> progress <span class="punc">})</span> <span class="punc">=></span> $value.textContent <span class="punc">=</span> Math.round(progress <span class="punc">*</span> <span class="num">100</span>) <span class="punc">+</span> <span class="str">'%'</span>
+<span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
             </div>
 
             <!-- HTML Code View -->
@@ -118,22 +125,20 @@ engine.defaults.onUpdate = self => console.log(self.id);
         }
 
         function runDemo() {
-            const $circle = document.querySelector('.circle');
+            const $ = animal;
             const $value = document.querySelector('.value');
+            const $circle = document.querySelector('.circle');
             $circle.style.transform = 'none';
             $value.textContent = '0';
-            
-            let updates = 0;
-            const [ valueEl ] = utils.$('.value');
-            
-            animal.animate('.circle', {
+
+            $('.circle').animate({
                 x: '16rem',
                 duration: 1000,
                 loop: Infinity, 
                 direction: 'alternate',
-                update: () => {
-                    valueEl.textContent = ++updates;
-                }
+                begin: () => { $value.textContent = '0'; },
+                update: ({ progress }) => { $value.textContent = Math.round(progress * 100) + '%'; },
+                complete: () => { /* not reached when loop is Infinity */ }
             });
         }
         

+ 9 - 5
doc/tween_value_types/test_colors.html

@@ -26,8 +26,12 @@
                 <div class="tabs"><div class="tab active">JavaScript</div></div>
             </div>
             <div id="js-code" class="code-view active">
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'.box'</span><span class="punc">,</span> <span class="punc">{</span>
-  backgroundColor<span class="punc">:</span> <span class="str">'#FFF'</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
+<span class="fun">$</span><span class="punc">(</span><span class="str">'.box'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">(</span><span class="punc">{</span>
+  backgroundColor<span class="punc">:</span> <span class="str">'#FFF'</span><span class="punc">,</span>
+  duration<span class="punc">:</span> <span class="num">1000</span><span class="punc">,</span>
+  direction<span class="punc">:</span> <span class="str">'alternate'</span><span class="punc">,</span>
+  loop<span class="punc">:</span> <span class="num">2</span>
 <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
             </div>
             <div class="demo-visual">
@@ -42,9 +46,9 @@
             const box = document.querySelector('.box');
             box.style.backgroundColor = ''; // Reset
             
-            // WAAPI handles color interpolation natively!
-            animal.animate('.box', { 
-                backgroundColor: '#FFF', 
+            const $ = animal;
+            $('.box').animate({
+                backgroundColor: '#FFF',
                 duration: 1000,
                 direction: 'alternate',
                 loop: 2

+ 8 - 4
doc/tween_value_types/test_numerical.html

@@ -26,9 +26,12 @@
                 <div class="tabs"><div class="tab active">JavaScript</div></div>
             </div>
             <div id="js-code" class="code-view active">
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'.box'</span><span class="punc">,</span> <span class="punc">{</span>
-  translateX<span class="punc">:</span> <span class="num">200</span><span class="punc">,</span>
-  scale<span class="punc">:</span> <span class="num">2</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
+<span class="fun">$</span><span class="punc">(</span><span class="str">'.box'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">(</span><span class="punc">{</span>
+  x<span class="punc">:</span> <span class="num">200</span><span class="punc">,</span>
+  scale<span class="punc">:</span> <span class="num">2</span><span class="punc">,</span>
+  duration<span class="punc">:</span> <span class="num">1000</span><span class="punc">,</span>
+  easing<span class="punc">:</span> <span class="str">'ease-out'</span>
 <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
             </div>
             <div class="demo-visual">
@@ -41,7 +44,8 @@
     <script>
         function runDemo() {
             document.querySelector('.box').style.transform = 'none';
-            animal.animate('.box', { translateX: 200, scale: 2, duration: 1000 });
+            const $ = animal;
+            $('.box').animate({ x: 200, scale: 2, duration: 1000, easing: 'ease-out' });
         }
         setTimeout(runDemo, 500);
     </script>

+ 21 - 22
doc/tween_value_types/test_relative.html

@@ -19,16 +19,24 @@
     <div class="container">
         <div style="font-size: 10px; font-weight: bold; letter-spacing: 1px; color: #ff4b4b; margin-bottom: 20px;">ANIMATION > TWEEN VALUE TYPES</div>
         <h1>Relative value</h1>
-        <p class="description">Add, subtract or multiply current value.</p>
+        <p class="description">
+            Animal.js does not support Anime.js style relative strings like <code class="inline">+=100</code> or <code class="inline">-=10px</code>.
+            If you need “relative” behavior, read the current value yourself and animate to the computed target value.
+        </p>
         <div class="box-container">
             <div class="box-header">
                 <div class="box-title">Relative value example</div>
                 <div class="tabs"><div class="tab active">JavaScript</div></div>
             </div>
             <div id="js-code" class="code-view active">
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'.box'</span><span class="punc">,</span> <span class="punc">{</span>
-  translateX<span class="punc">:</span> <span class="str">'+=100'</span><span class="punc">,</span>
-  width<span class="punc">:</span> <span class="str">'-=10px'</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
+<span class="kwd">const</span> box <span class="punc">=</span> document.querySelector<span class="punc">(</span><span class="str">'.box'</span><span class="punc">);</span>
+<span class="kwd">const</span> w <span class="punc">=</span> parseFloat<span class="punc">(</span>getComputedStyle<span class="punc">(</span>box<span class="punc">)</span>.width<span class="punc">)</span> <span class="punc">||</span> <span class="num">50</span><span class="punc">;</span>
+
+<span class="fun">$</span><span class="punc">(</span>box<span class="punc">)</span>.<span class="fun">animate</span><span class="punc">(</span><span class="punc">{</span>
+  x<span class="punc">:</span> <span class="num">100</span><span class="punc">,</span>
+  width<span class="punc">:</span> <span class="punc">[</span><span class="str">''</span> <span class="punc">+</span> w <span class="punc">+</span> <span class="str">'px'</span><span class="punc">,</span> <span class="str">''</span> <span class="punc">+</span> <span class="punc">(</span>w <span class="punc">-</span> <span class="num">10</span><span class="punc">)</span> <span class="punc">+</span> <span class="str">'px'</span><span class="punc">]</span><span class="punc">,</span>
+  duration<span class="punc">:</span> <span class="num">1000</span>
 <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
             </div>
             <div class="demo-visual">
@@ -40,27 +48,18 @@
     <script src="../../animal.js"></script>
     <script>
         function runDemo() {
-            // Animal.js 'animate' currently doesn't natively support += / -= parsing inside value strings in this lite version
-            // unless implemented in `getUnit` or pre-processing.
-            // The Timeline implementation supports offset "+=200", but property values might not.
-            // Let's check `animal.js`.
-            // The `animate` function logic: `const endVal = targetVal;`
-            // It parses numbers but doesn't explicitly look for +=.
-            
-            // For demo purposes, we will simulate it or rely on WAAPI if it supports it?
-            // WAAPI does NOT support relative values natively in keyframes.
-            // Anime.js does pre-calculation.
-            
-            // Simulation:
             const box = document.querySelector('.box');
             box.style.transform = 'none';
             box.style.width = '50px';
-            
-            // We'll just animate to absolute values that represent the relative change
-            animal.animate('.box', { 
-                translateX: 100, // +100 from 0
-                width: '40px',   // -10 from 50
-                duration: 1000 
+
+            const $ = animal;
+            const w = parseFloat(getComputedStyle(box).width) || 50;
+
+            $(box).animate({
+                x: 100,
+                width: [w + 'px', (w - 10) + 'px'],
+                duration: 1000,
+                easing: 'ease-out'
             });
         }
         setTimeout(runDemo, 500);

+ 10 - 9
doc/tween_value_types/test_unit.html

@@ -26,8 +26,11 @@
                 <div class="tabs"><div class="tab active">JavaScript</div></div>
             </div>
             <div id="js-code" class="code-view active">
-<span class="fun">animate</span><span class="punc">(</span><span class="str">'.box'</span><span class="punc">,</span> <span class="punc">{</span>
-  width<span class="punc">:</span> <span class="str">'100%'</span> <span class="com">// from 50px</span>
+<span class="kwd">const</span> $ = <span class="fun">animal</span><span class="punc">;</span>
+<span class="fun">$</span><span class="punc">(</span><span class="str">'.box'</span><span class="punc">)</span>.<span class="fun">animate</span><span class="punc">(</span><span class="punc">{</span>
+  width<span class="punc">:</span> <span class="str">'100%'</span><span class="punc">,</span> <span class="com">// from 50px</span>
+  duration<span class="punc">:</span> <span class="num">1000</span><span class="punc">,</span>
+  easing<span class="punc">:</span> <span class="str">'ease-in-out'</span>
 <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
             </div>
             <div class="demo-visual" style="display: block; padding: 40px 0;">
@@ -42,13 +45,11 @@
             const box = document.querySelector('.box');
             box.style.width = '50px'; // Reset
             
-            // WAAPI can interpolate units if browser supports it layout-dependently?
-            // Often mixed units (px -> %) work in WAAPI for transforms, but for layout properties it might snap.
-            // Let's test.
-            
-            animal.animate('.box', { 
-                width: '100%', 
-                duration: 1000 
+            const $ = animal;
+            $('.box').animate({
+                width: '100%',
+                duration: 1000,
+                easing: 'ease-in-out'
             });
         }
         setTimeout(runDemo, 500);

+ 64 - 6
layer.js

@@ -1,7 +1,23 @@
 (function (global, factory) {
-  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
-    typeof define === 'function' && define.amd ? define(factory) :
-      (global.Layer = factory());
+  const LayerClass = factory();
+  // Allow usage as `Layer({...})` or `new Layer()`
+  // But Layer is a class. We can wrap it in a proxy or factory function.
+  
+  function LayerFactory(options) {
+     if (options && typeof options === 'object') {
+         return LayerClass.$(options);
+     }
+     return new LayerClass();
+  }
+  
+  // Copy static methods
+  Object.assign(LayerFactory, LayerClass);
+  // Also copy prototype for instanceof checks if needed (though tricky with factory)
+  LayerFactory.prototype = LayerClass.prototype;
+  
+  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = LayerFactory :
+    typeof define === 'function' && define.amd ? define(() => LayerFactory) :
+      (global.Layer = LayerFactory);
 }(this, (function () {
   'use strict';
 
@@ -200,11 +216,42 @@
       this.reject = null;
     }
 
+    // Constructor helper when called as function: const popup = Layer({...})
+    static get isProxy() { return true; }
+    
     // Static entry point
     static fire(options) {
       const instance = new Layer();
       return instance._fire(options);
     }
+    
+    // Chainable entry point (builder-style)
+    // Example:
+    //   Layer.$({ title: 'Hi' }).fire().then(...)
+    //   Layer.$().config({ title: 'Hi' }).fire()
+    static $(options) {
+      const instance = new Layer();
+      if (options !== undefined) instance.config(options);
+      return instance;
+    }
+    
+    // Chainable config helper (does not render until `.fire()` is called)
+    config(options = {}) {
+      // Support the same shorthand as Layer.fire(title, text, icon)
+      if (typeof options === 'string') {
+        options = { title: options };
+        if (arguments[1]) options.text = arguments[1];
+        if (arguments[2]) options.icon = arguments[2];
+      }
+      this.params = { ...(this.params || {}), ...options };
+      return this;
+    }
+    
+    // Instance entry point (chainable)
+    fire(options) {
+      const merged = (options === undefined) ? (this.params || {}) : options;
+      return this._fire(merged);
+    }
 
     _fire(options = {}) {
       if (typeof options === 'string') {
@@ -263,11 +310,22 @@
         this.dom.popup.appendChild(this.dom.title);
       }
 
-      // Text
-      if (this.params.text) {
+      // Content (Text / HTML / Element)
+      if (this.params.text || this.params.html || this.params.content) {
         this.dom.content = document.createElement('div');
         this.dom.content.className = `${PREFIX}content`;
-        this.dom.content.textContent = this.params.text;
+        
+        if (this.params.content) {
+           // DOM Element or Selector
+           let el = this.params.content;
+           if (typeof el === 'string') el = document.querySelector(el);
+           if (el instanceof Element) this.dom.content.appendChild(el); 
+        } else if (this.params.html) {
+           this.dom.content.innerHTML = this.params.html;
+        } else {
+           this.dom.content.textContent = this.params.text;
+        }
+        
         this.dom.popup.appendChild(this.dom.content);
       }
 

+ 21 - 0
xjs.js

@@ -0,0 +1,21 @@
+// xjs.js - Combined build (example)
+// This file is auto-generated/merged in production.
+// For dev, it imports animal and layer from window if loaded separately.
+
+(function(global) {
+  // If we are in a merged environment, Layer and animal might be defined in closures above.
+  // But here we simulate the final structure.
+  
+  // Ensure we have a global entry point if not already
+  const x = global.animal || {};
+  
+  // Extend animal with Layer features if not present
+  if (global.Layer && !x.Layer) {
+    x.Layer = global.Layer;
+  }
+  
+  // Export to global 'x' or 'xjs' as short alias?
+  // global.x = x; 
+  
+})(typeof window !== 'undefined' ? window : this);
+

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov