niahere 0.2.61 → 0.2.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/defaults/memory-promoter.md +99 -0
- package/defaults/self/staging.md +48 -0
- package/package.json +7 -3
- package/skills/code-review/pr-review.md +14 -1
- package/skills/cro/page.md +22 -0
- package/skills/frontend-design/SKILL.md +7 -3
- package/skills/frontend-design/building.md +17 -2
- package/skills/qa/SKILL.md +108 -72
- package/skills/userinterface-wiki/AGENTS.md +3749 -0
- package/skills/userinterface-wiki/SKILL.md +253 -0
- package/skills/userinterface-wiki/metadata.json +26 -0
- package/skills/userinterface-wiki/rules/_sections.md +66 -0
- package/skills/userinterface-wiki/rules/_template.md +24 -0
- package/skills/userinterface-wiki/rules/a11y-reduced-motion-check.md +30 -0
- package/skills/userinterface-wiki/rules/a11y-toggle-setting.md +30 -0
- package/skills/userinterface-wiki/rules/a11y-visual-equivalent.md +36 -0
- package/skills/userinterface-wiki/rules/a11y-volume-control.md +28 -0
- package/skills/userinterface-wiki/rules/appropriate-confirmations-only.md +19 -0
- package/skills/userinterface-wiki/rules/appropriate-errors-warnings.md +18 -0
- package/skills/userinterface-wiki/rules/appropriate-no-decorative.md +21 -0
- package/skills/userinterface-wiki/rules/appropriate-no-high-frequency.md +28 -0
- package/skills/userinterface-wiki/rules/appropriate-no-punishing.md +27 -0
- package/skills/userinterface-wiki/rules/container-callback-ref.md +31 -0
- package/skills/userinterface-wiki/rules/container-guard-initial-zero.md +25 -0
- package/skills/userinterface-wiki/rules/container-no-excessive-use.md +13 -0
- package/skills/userinterface-wiki/rules/container-overflow-hidden.md +25 -0
- package/skills/userinterface-wiki/rules/container-transition-delay.md +21 -0
- package/skills/userinterface-wiki/rules/container-two-div-pattern.md +35 -0
- package/skills/userinterface-wiki/rules/container-use-resize-observer.md +48 -0
- package/skills/userinterface-wiki/rules/context-cleanup-nodes.md +25 -0
- package/skills/userinterface-wiki/rules/context-resume-suspended.md +28 -0
- package/skills/userinterface-wiki/rules/context-reuse-single.md +30 -0
- package/skills/userinterface-wiki/rules/design-filter-for-character.md +25 -0
- package/skills/userinterface-wiki/rules/design-noise-for-percussion.md +26 -0
- package/skills/userinterface-wiki/rules/design-oscillator-for-tonal.md +22 -0
- package/skills/userinterface-wiki/rules/duration-max-300ms.md +21 -0
- package/skills/userinterface-wiki/rules/duration-press-hover.md +21 -0
- package/skills/userinterface-wiki/rules/duration-shorten-before-curve.md +21 -0
- package/skills/userinterface-wiki/rules/duration-small-state.md +15 -0
- package/skills/userinterface-wiki/rules/easing-entrance-ease-out.md +21 -0
- package/skills/userinterface-wiki/rules/easing-exit-ease-in.md +21 -0
- package/skills/userinterface-wiki/rules/easing-for-state-change.md +27 -0
- package/skills/userinterface-wiki/rules/easing-linear-only-progress.md +21 -0
- package/skills/userinterface-wiki/rules/easing-natural-decay.md +22 -0
- package/skills/userinterface-wiki/rules/easing-no-linear-motion.md +22 -0
- package/skills/userinterface-wiki/rules/easing-transition-ease-in-out.md +15 -0
- package/skills/userinterface-wiki/rules/envelope-exponential-decay.md +21 -0
- package/skills/userinterface-wiki/rules/envelope-no-zero-target.md +21 -0
- package/skills/userinterface-wiki/rules/envelope-set-initial-value.md +22 -0
- package/skills/userinterface-wiki/rules/exit-key-required.md +29 -0
- package/skills/userinterface-wiki/rules/exit-matches-initial.md +29 -0
- package/skills/userinterface-wiki/rules/exit-prop-required.md +33 -0
- package/skills/userinterface-wiki/rules/exit-requires-wrapper.md +27 -0
- package/skills/userinterface-wiki/rules/impl-default-subtle.md +21 -0
- package/skills/userinterface-wiki/rules/impl-preload-audio.md +34 -0
- package/skills/userinterface-wiki/rules/impl-reset-current-time.md +26 -0
- package/skills/userinterface-wiki/rules/mode-pop-layout-for-lists.md +25 -0
- package/skills/userinterface-wiki/rules/mode-sync-layout-conflict.md +29 -0
- package/skills/userinterface-wiki/rules/mode-wait-doubles-duration.md +25 -0
- package/skills/userinterface-wiki/rules/morphing-aria-hidden.md +21 -0
- package/skills/userinterface-wiki/rules/morphing-consistent-viewbox.md +23 -0
- package/skills/userinterface-wiki/rules/morphing-group-variants.md +33 -0
- package/skills/userinterface-wiki/rules/morphing-jump-non-grouped.md +29 -0
- package/skills/userinterface-wiki/rules/morphing-reduced-motion.md +28 -0
- package/skills/userinterface-wiki/rules/morphing-spring-rotation.md +23 -0
- package/skills/userinterface-wiki/rules/morphing-strokelinecap-round.md +21 -0
- package/skills/userinterface-wiki/rules/morphing-three-lines.md +32 -0
- package/skills/userinterface-wiki/rules/morphing-use-collapsed.md +33 -0
- package/skills/userinterface-wiki/rules/native-backdrop-styling.md +27 -0
- package/skills/userinterface-wiki/rules/native-placeholder-styling.md +27 -0
- package/skills/userinterface-wiki/rules/native-selection-styling.md +18 -0
- package/skills/userinterface-wiki/rules/nested-consistent-timing.md +25 -0
- package/skills/userinterface-wiki/rules/nested-propagate-required.md +41 -0
- package/skills/userinterface-wiki/rules/none-context-menu-entrance.md +25 -0
- package/skills/userinterface-wiki/rules/none-high-frequency.md +29 -0
- package/skills/userinterface-wiki/rules/none-keyboard-navigation.md +32 -0
- package/skills/userinterface-wiki/rules/param-click-duration.md +21 -0
- package/skills/userinterface-wiki/rules/param-filter-frequency-range.md +21 -0
- package/skills/userinterface-wiki/rules/param-q-value-range.md +21 -0
- package/skills/userinterface-wiki/rules/param-reasonable-gain.md +21 -0
- package/skills/userinterface-wiki/rules/physics-active-state.md +23 -0
- package/skills/userinterface-wiki/rules/physics-no-excessive-stagger.md +22 -0
- package/skills/userinterface-wiki/rules/physics-spring-for-overshoot.md +23 -0
- package/skills/userinterface-wiki/rules/physics-subtle-deformation.md +22 -0
- package/skills/userinterface-wiki/rules/prefetch-hit-slop.md +27 -0
- package/skills/userinterface-wiki/rules/prefetch-keyboard-tab.md +19 -0
- package/skills/userinterface-wiki/rules/prefetch-not-everything.md +22 -0
- package/skills/userinterface-wiki/rules/prefetch-touch-fallback.md +34 -0
- package/skills/userinterface-wiki/rules/prefetch-trajectory-over-hover.md +32 -0
- package/skills/userinterface-wiki/rules/prefetch-use-selectively.md +13 -0
- package/skills/userinterface-wiki/rules/presence-disable-interactions.md +31 -0
- package/skills/userinterface-wiki/rules/presence-hook-in-child.md +31 -0
- package/skills/userinterface-wiki/rules/presence-safe-to-remove.md +37 -0
- package/skills/userinterface-wiki/rules/pseudo-content-required.md +28 -0
- package/skills/userinterface-wiki/rules/pseudo-first-line-styling.md +27 -0
- package/skills/userinterface-wiki/rules/pseudo-hit-target-expansion.md +31 -0
- package/skills/userinterface-wiki/rules/pseudo-marker-styling.md +28 -0
- package/skills/userinterface-wiki/rules/pseudo-over-dom-node.md +32 -0
- package/skills/userinterface-wiki/rules/pseudo-position-relative-parent.md +33 -0
- package/skills/userinterface-wiki/rules/pseudo-z-index-layering.md +37 -0
- package/skills/userinterface-wiki/rules/spring-for-gestures.md +27 -0
- package/skills/userinterface-wiki/rules/spring-for-interruptible.md +27 -0
- package/skills/userinterface-wiki/rules/spring-params-balanced.md +29 -0
- package/skills/userinterface-wiki/rules/spring-preserves-velocity.md +28 -0
- package/skills/userinterface-wiki/rules/staging-dim-background.md +22 -0
- package/skills/userinterface-wiki/rules/staging-one-focal-point.md +24 -0
- package/skills/userinterface-wiki/rules/staging-z-index-hierarchy.md +22 -0
- package/skills/userinterface-wiki/rules/timing-consistent.md +24 -0
- package/skills/userinterface-wiki/rules/timing-no-entrance-context-menu.md +22 -0
- package/skills/userinterface-wiki/rules/timing-under-300ms.md +22 -0
- package/skills/userinterface-wiki/rules/transition-name-cleanup.md +28 -0
- package/skills/userinterface-wiki/rules/transition-name-required.md +27 -0
- package/skills/userinterface-wiki/rules/transition-name-unique.md +24 -0
- package/skills/userinterface-wiki/rules/transition-over-js-library.md +32 -0
- package/skills/userinterface-wiki/rules/transition-style-pseudo-elements.md +24 -0
- package/skills/userinterface-wiki/rules/type-antialiased-on-retina.md +18 -0
- package/skills/userinterface-wiki/rules/type-disambiguation-stylistic-set.md +15 -0
- package/skills/userinterface-wiki/rules/type-font-display-swap.md +28 -0
- package/skills/userinterface-wiki/rules/type-justify-with-hyphens.md +24 -0
- package/skills/userinterface-wiki/rules/type-letter-spacing-uppercase.md +28 -0
- package/skills/userinterface-wiki/rules/type-no-font-synthesis.md +18 -0
- package/skills/userinterface-wiki/rules/type-oldstyle-nums-for-prose.md +21 -0
- package/skills/userinterface-wiki/rules/type-opentype-contextual-alternates.md +15 -0
- package/skills/userinterface-wiki/rules/type-optical-sizing-auto.md +25 -0
- package/skills/userinterface-wiki/rules/type-proper-fractions.md +15 -0
- package/skills/userinterface-wiki/rules/type-slashed-zero.md +17 -0
- package/skills/userinterface-wiki/rules/type-tabular-nums-for-data.md +21 -0
- package/skills/userinterface-wiki/rules/type-text-wrap-balance-headings.md +21 -0
- package/skills/userinterface-wiki/rules/type-text-wrap-pretty.md +16 -0
- package/skills/userinterface-wiki/rules/type-underline-offset.md +25 -0
- package/skills/userinterface-wiki/rules/type-variable-weight-continuous.md +23 -0
- package/skills/userinterface-wiki/rules/ux-aesthetic-usability.md +32 -0
- package/skills/userinterface-wiki/rules/ux-cognitive-load-reduce.md +49 -0
- package/skills/userinterface-wiki/rules/ux-common-region-boundaries.md +50 -0
- package/skills/userinterface-wiki/rules/ux-doherty-perceived-speed.md +29 -0
- package/skills/userinterface-wiki/rules/ux-doherty-under-400ms.md +30 -0
- package/skills/userinterface-wiki/rules/ux-fitts-hit-area.md +32 -0
- package/skills/userinterface-wiki/rules/ux-fitts-target-size.md +31 -0
- package/skills/userinterface-wiki/rules/ux-goal-gradient-progress.md +33 -0
- package/skills/userinterface-wiki/rules/ux-hicks-minimize-choices.md +45 -0
- package/skills/userinterface-wiki/rules/ux-jakobs-familiar-patterns.md +37 -0
- package/skills/userinterface-wiki/rules/ux-millers-chunking.md +23 -0
- package/skills/userinterface-wiki/rules/ux-pareto-prioritize-features.md +36 -0
- package/skills/userinterface-wiki/rules/ux-peak-end-finish-strong.md +35 -0
- package/skills/userinterface-wiki/rules/ux-postels-accept-messy-input.md +45 -0
- package/skills/userinterface-wiki/rules/ux-pragnanz-simplify.md +33 -0
- package/skills/userinterface-wiki/rules/ux-progressive-disclosure.md +41 -0
- package/skills/userinterface-wiki/rules/ux-proximity-grouping.md +38 -0
- package/skills/userinterface-wiki/rules/ux-serial-position.md +31 -0
- package/skills/userinterface-wiki/rules/ux-similarity-consistency.md +35 -0
- package/skills/userinterface-wiki/rules/ux-teslers-complexity.md +28 -0
- package/skills/userinterface-wiki/rules/ux-uniform-connectedness.md +43 -0
- package/skills/userinterface-wiki/rules/ux-von-restorff-emphasis.md +29 -0
- package/skills/userinterface-wiki/rules/ux-zeigarnik-show-incomplete.md +36 -0
- package/skills/userinterface-wiki/rules/visual-animate-shadow-pseudo.md +49 -0
- package/skills/userinterface-wiki/rules/visual-border-alpha-colors.md +25 -0
- package/skills/userinterface-wiki/rules/visual-button-shadow-anatomy.md +49 -0
- package/skills/userinterface-wiki/rules/visual-concentric-radius.md +40 -0
- package/skills/userinterface-wiki/rules/visual-consistent-spacing-scale.md +35 -0
- package/skills/userinterface-wiki/rules/visual-layered-shadows.md +30 -0
- package/skills/userinterface-wiki/rules/visual-no-pure-black-shadow.md +25 -0
- package/skills/userinterface-wiki/rules/visual-shadow-direction.md +25 -0
- package/skills/userinterface-wiki/rules/visual-shadow-matches-elevation.md +23 -0
- package/skills/userinterface-wiki/rules/weight-duration-matches-action.md +29 -0
- package/skills/userinterface-wiki/rules/weight-match-action.md +32 -0
- package/src/cli/index.ts +23 -73
- package/src/cli/job.ts +25 -92
- package/src/cli/status.ts +17 -9
- package/src/commands/init.ts +1 -0
- package/src/commands/validate.ts +12 -10
- package/src/core/agents.ts +6 -19
- package/src/core/consolidator.ts +97 -91
- package/src/core/daemon.ts +71 -43
- package/src/core/finalizer.ts +31 -3
- package/src/core/health.ts +5 -17
- package/src/core/runner.ts +8 -44
- package/src/core/scheduler.ts +12 -49
- package/src/core/skills.ts +4 -11
- package/src/core/summarizer.ts +7 -21
- package/src/db/connection.ts +0 -11
- package/src/db/models/job.ts +23 -22
- package/src/db/with-db.ts +11 -0
- package/src/mcp/server.ts +1 -1
- package/src/prompts/environment.md +44 -41
- package/src/utils/pid.ts +44 -0
- package/src/utils/schedule.ts +39 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Animated Bounds Sparingly
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: container, performance, restraint
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Use Animated Bounds Sparingly
|
|
8
|
+
|
|
9
|
+
Animated bounds is a subtle effect. Use it for buttons, accordions, and interactive elements — not everywhere.
|
|
10
|
+
|
|
11
|
+
**Good use cases:** loading state buttons, expandable sections, accordions, FAQs, content reveals.
|
|
12
|
+
|
|
13
|
+
**Bad use cases:** every container on the page, static layouts, elements that don't change size.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Overflow Hidden on Animated Container
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: container, overflow, clip
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overflow Hidden on Animated Container
|
|
8
|
+
|
|
9
|
+
Set overflow: hidden on the animated outer container to clip content during size transitions.
|
|
10
|
+
|
|
11
|
+
**Incorrect (content overflows during animation):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<motion.div animate={{ height: bounds.height }}>
|
|
15
|
+
<div ref={ref}>{children}</div>
|
|
16
|
+
</motion.div>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (clipped during transition):**
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<motion.div animate={{ height: bounds.height }} style={{ overflow: "hidden" }}>
|
|
23
|
+
<div ref={ref}>{children}</div>
|
|
24
|
+
</motion.div>
|
|
25
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Add Delay for Natural Container Transitions
|
|
3
|
+
impact: LOW
|
|
4
|
+
tags: container, delay, transition, natural
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Add Delay for Natural Container Transitions
|
|
8
|
+
|
|
9
|
+
Add a small delay to container size animations so the transition feels like it's catching up to the content.
|
|
10
|
+
|
|
11
|
+
**Correct:**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<motion.div
|
|
15
|
+
animate={{ height: bounds.height }}
|
|
16
|
+
transition={{ duration: 0.2, delay: 0.05 }}
|
|
17
|
+
style={{ overflow: "hidden" }}
|
|
18
|
+
>
|
|
19
|
+
<div ref={ref}>{children}</div>
|
|
20
|
+
</motion.div>
|
|
21
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Two-Div Pattern for Animated Bounds
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: container, measure, motion
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Two-Div Pattern for Animated Bounds
|
|
8
|
+
|
|
9
|
+
Use an outer animated div and an inner measured div. Never measure and animate the same element.
|
|
10
|
+
|
|
11
|
+
**Incorrect (measure and animate same element — creates feedback loop):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
function AnimatedContainer({ children }) {
|
|
15
|
+
const [ref, bounds] = useMeasure();
|
|
16
|
+
return (
|
|
17
|
+
<motion.div ref={ref} animate={{ height: bounds.height }}>
|
|
18
|
+
{children}
|
|
19
|
+
</motion.div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Correct (separate measure and animate targets):**
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
function AnimatedContainer({ children }) {
|
|
28
|
+
const [ref, bounds] = useMeasure();
|
|
29
|
+
return (
|
|
30
|
+
<motion.div animate={{ height: bounds.height }}>
|
|
31
|
+
<div ref={ref}>{children}</div>
|
|
32
|
+
</motion.div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use ResizeObserver for Measurement
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: container, resize-observer, performance
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Use ResizeObserver for Measurement
|
|
8
|
+
|
|
9
|
+
Use ResizeObserver to track element dimensions. It fires on resize without causing layout thrashing.
|
|
10
|
+
|
|
11
|
+
**Incorrect (measuring on every render):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
function useMeasure(ref) {
|
|
15
|
+
const [bounds, setBounds] = useState({ width: 0, height: 0 });
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (ref.current) {
|
|
18
|
+
const rect = ref.current.getBoundingClientRect();
|
|
19
|
+
setBounds({ width: rect.width, height: rect.height });
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return bounds;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Correct (ResizeObserver):**
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
function useMeasure() {
|
|
30
|
+
const [element, setElement] = useState(null);
|
|
31
|
+
const [bounds, setBounds] = useState({ width: 0, height: 0 });
|
|
32
|
+
const ref = useCallback((node) => setElement(node), []);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (!element) return;
|
|
36
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
37
|
+
setBounds({
|
|
38
|
+
width: entry.contentRect.width,
|
|
39
|
+
height: entry.contentRect.height,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
observer.observe(element);
|
|
43
|
+
return () => observer.disconnect();
|
|
44
|
+
}, [element]);
|
|
45
|
+
|
|
46
|
+
return [ref, bounds];
|
|
47
|
+
}
|
|
48
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Clean Up Audio Nodes After Playback
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: context, cleanup, disconnect
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Clean Up Audio Nodes After Playback
|
|
8
|
+
|
|
9
|
+
Disconnect and clean up audio nodes after playback.
|
|
10
|
+
|
|
11
|
+
**Incorrect (nodes remain connected):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
source.start();
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (cleaned up on end):**
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
source.start();
|
|
21
|
+
source.onended = () => {
|
|
22
|
+
source.disconnect();
|
|
23
|
+
gain.disconnect();
|
|
24
|
+
};
|
|
25
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Resume Suspended AudioContext
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: context, resume, suspended
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Resume Suspended AudioContext
|
|
8
|
+
|
|
9
|
+
Check and resume suspended AudioContext before playing.
|
|
10
|
+
|
|
11
|
+
**Incorrect (plays without checking):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
function playSound() {
|
|
15
|
+
const ctx = getAudioContext();
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (resumes if suspended):**
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
function playSound() {
|
|
23
|
+
const ctx = getAudioContext();
|
|
24
|
+
if (ctx.state === "suspended") {
|
|
25
|
+
ctx.resume();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Reuse Single AudioContext
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: context, audio-context, singleton
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Reuse Single AudioContext
|
|
8
|
+
|
|
9
|
+
Reuse a single AudioContext instance; do not create new ones per sound.
|
|
10
|
+
|
|
11
|
+
**Incorrect (new context per call):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
function playSound() {
|
|
15
|
+
const ctx = new AudioContext();
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (singleton):**
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
let audioContext: AudioContext | null = null;
|
|
23
|
+
|
|
24
|
+
function getAudioContext(): AudioContext {
|
|
25
|
+
if (!audioContext) {
|
|
26
|
+
audioContext = new AudioContext();
|
|
27
|
+
}
|
|
28
|
+
return audioContext;
|
|
29
|
+
}
|
|
30
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Bandpass Filter for Sound Character
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: design, filter, bandpass
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Bandpass Filter for Sound Character
|
|
8
|
+
|
|
9
|
+
Apply bandpass filter to shape percussive sounds.
|
|
10
|
+
|
|
11
|
+
**Incorrect (raw noise):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
source.connect(gain).connect(ctx.destination);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (filtered noise):**
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
const filter = ctx.createBiquadFilter();
|
|
21
|
+
filter.type = "bandpass";
|
|
22
|
+
filter.frequency.value = 4000;
|
|
23
|
+
filter.Q.value = 3;
|
|
24
|
+
source.connect(filter).connect(gain).connect(ctx.destination);
|
|
25
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Noise for Percussive Sounds
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: design, noise, percussion
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Noise for Percussive Sounds
|
|
8
|
+
|
|
9
|
+
Use filtered noise for clicks/taps, not oscillators.
|
|
10
|
+
|
|
11
|
+
**Incorrect (oscillator for click):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
const osc = ctx.createOscillator();
|
|
15
|
+
osc.type = "sine";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (noise burst for click):**
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
const buffer = ctx.createBuffer(1, ctx.sampleRate * 0.008, ctx.sampleRate);
|
|
22
|
+
const data = buffer.getChannelData(0);
|
|
23
|
+
for (let i = 0; i < data.length; i++) {
|
|
24
|
+
data[i] = (Math.random() * 2 - 1) * Math.exp(-i / 50);
|
|
25
|
+
}
|
|
26
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Oscillators for Tonal Sounds
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: design, oscillator, tonal
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Oscillators for Tonal Sounds
|
|
8
|
+
|
|
9
|
+
Use oscillators with pitch movement for tonal sounds (pops, confirmations).
|
|
10
|
+
|
|
11
|
+
**Incorrect (static frequency):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
osc.frequency.value = 400;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (pitch sweep):**
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
osc.frequency.setValueAtTime(400, t);
|
|
21
|
+
osc.frequency.exponentialRampToValueAtTime(600, t + 0.04);
|
|
22
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Max 300ms for User Actions
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: duration, max, user-action
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Max 300ms for User Actions
|
|
8
|
+
|
|
9
|
+
User-initiated animations must not exceed 300ms.
|
|
10
|
+
|
|
11
|
+
**Incorrect (exceeds limit):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<motion.div transition={{ duration: 0.5 }} />
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (within limit):**
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
<motion.div transition={{ duration: 0.25 }} />
|
|
21
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Press and Hover 120-180ms
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: duration, press, hover
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Press and Hover 120-180ms
|
|
8
|
+
|
|
9
|
+
Press and hover interactions should use 120-180ms duration.
|
|
10
|
+
|
|
11
|
+
**Incorrect (too slow):**
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
.button:hover { transition: background-color 400ms; }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (appropriate duration):**
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
.button:hover { transition: background-color 150ms; }
|
|
21
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shorten Duration Before Adjusting Curve
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: duration, optimization
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Shorten Duration Before Adjusting Curve
|
|
8
|
+
|
|
9
|
+
If animation feels slow, shorten duration before adjusting curve.
|
|
10
|
+
|
|
11
|
+
**Incorrect (adjusting curve instead):**
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
.element { transition: 400ms cubic-bezier(0, 0.9, 0.1, 1); }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (shorter duration):**
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
.element { transition: 200ms ease-out; }
|
|
21
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Small State Changes 180-260ms
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: duration, state-change
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Small State Changes 180-260ms
|
|
8
|
+
|
|
9
|
+
Small state changes should use 180-260ms duration.
|
|
10
|
+
|
|
11
|
+
**Correct:**
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
.toggle { transition: transform 200ms ease; }
|
|
15
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Ease-Out for Entrances
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: easing, entrance, ease-out
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Ease-Out for Entrances
|
|
8
|
+
|
|
9
|
+
Entrances must use ease-out (arrive fast, settle gently).
|
|
10
|
+
|
|
11
|
+
**Incorrect (ease-in for entrance):**
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
.modal-enter { animation-timing-function: ease-in; }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (ease-out for entrance):**
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
.modal-enter { animation-timing-function: ease-out; }
|
|
21
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Ease-In for Exits
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: easing, exit, ease-in
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Ease-In for Exits
|
|
8
|
+
|
|
9
|
+
Exits must use ease-in (build momentum before departure).
|
|
10
|
+
|
|
11
|
+
**Incorrect (ease-out for exit):**
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
.modal-exit { animation-timing-function: ease-out; }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (ease-in for exit):**
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
.modal-exit { animation-timing-function: ease-in; }
|
|
21
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Easing for System State Changes
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: easing, state-change, system
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Easing for System State Changes
|
|
8
|
+
|
|
9
|
+
System-initiated state changes should use easing curves.
|
|
10
|
+
|
|
11
|
+
**Incorrect (spring for announcement):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<motion.div
|
|
15
|
+
animate={{ y: 0 }}
|
|
16
|
+
transition={{ type: "spring" }}
|
|
17
|
+
/>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (easing for announcement):**
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
<motion.div
|
|
24
|
+
animate={{ y: 0 }}
|
|
25
|
+
transition={{ duration: 0.2, ease: "easeOut" }}
|
|
26
|
+
/>
|
|
27
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Linear Easing Only for Progress
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: easing, linear, progress
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Linear Easing Only for Progress
|
|
8
|
+
|
|
9
|
+
Linear easing only for progress bars and time representation.
|
|
10
|
+
|
|
11
|
+
**Incorrect (linear for motion):**
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
.card-slide { transition: transform 200ms linear; }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (linear for progress):**
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
.progress-bar { transition: width 100ms linear; }
|
|
21
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Exponential Ramps for Natural Decay
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Linear ramps for decay produce abrupt, unnatural cutoffs; exponential ramps match perceptual expectations.
|
|
5
|
+
tags: easing, decay, audio, animation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Exponential Ramps for Natural Decay
|
|
9
|
+
|
|
10
|
+
Use exponential ramps, not linear, for natural decay.
|
|
11
|
+
|
|
12
|
+
**Incorrect (linear ramp):**
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
gain.gain.linearRampToValueAtTime(0, t + 0.05);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (exponential ramp):**
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);
|
|
22
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: No Linear Easing for Motion
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Linear easing for motion feels mechanical; reserve it for progress indicators where uniform speed is expected.
|
|
5
|
+
tags: easing, linear, animation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## No Linear Easing for Motion
|
|
9
|
+
|
|
10
|
+
Linear easing should only be used for progress indicators, not motion.
|
|
11
|
+
|
|
12
|
+
**Incorrect (linear for motion):**
|
|
13
|
+
|
|
14
|
+
```css
|
|
15
|
+
.card { transition: transform 200ms linear; }
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (linear for progress only):**
|
|
19
|
+
|
|
20
|
+
```css
|
|
21
|
+
.progress-bar { transition: width 100ms linear; }
|
|
22
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Ease-In-Out for View Transitions
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: easing, transition, ease-in-out
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Ease-In-Out for View Transitions
|
|
8
|
+
|
|
9
|
+
View/mode transitions use ease-in-out for neutral attention.
|
|
10
|
+
|
|
11
|
+
**Correct:**
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
.page-transition { animation-timing-function: ease-in-out; }
|
|
15
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Exponential Decay for Natural Sound
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: envelope, decay, exponential
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Exponential Decay for Natural Sound
|
|
8
|
+
|
|
9
|
+
Use exponential ramps for natural decay, not linear.
|
|
10
|
+
|
|
11
|
+
**Incorrect (linear ramp):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
gain.gain.linearRampToValueAtTime(0, t + 0.05);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (exponential ramp):**
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);
|
|
21
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: No Zero Target for Exponential Ramps
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: envelope, exponential, zero
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## No Zero Target for Exponential Ramps
|
|
8
|
+
|
|
9
|
+
Exponential ramps cannot target 0; use 0.001 or similar small value.
|
|
10
|
+
|
|
11
|
+
**Incorrect (targets zero):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
gain.gain.exponentialRampToValueAtTime(0, t + 0.05);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (targets near-zero):**
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);
|
|
21
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Set Initial Value Before Ramp
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: envelope, initial, glitch
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Set Initial Value Before Ramp
|
|
8
|
+
|
|
9
|
+
Set initial value before ramping to avoid glitches.
|
|
10
|
+
|
|
11
|
+
**Incorrect (no initial value):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (initial value set):**
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
gain.gain.setValueAtTime(0.3, t);
|
|
21
|
+
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);
|
|
22
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Unique Keys in AnimatePresence Lists
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: exit, key, list
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Unique Keys in AnimatePresence Lists
|
|
8
|
+
|
|
9
|
+
Dynamic lists inside AnimatePresence must have unique keys.
|
|
10
|
+
|
|
11
|
+
**Incorrect (index as key):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<AnimatePresence>
|
|
15
|
+
{items.map((item, index) => (
|
|
16
|
+
<motion.div key={index} exit={{ opacity: 0 }} />
|
|
17
|
+
))}
|
|
18
|
+
</AnimatePresence>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (stable unique key):**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
<AnimatePresence>
|
|
25
|
+
{items.map((item) => (
|
|
26
|
+
<motion.div key={item.id} exit={{ opacity: 0 }} />
|
|
27
|
+
))}
|
|
28
|
+
</AnimatePresence>
|
|
29
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Exit Mirrors Initial for Symmetry
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: exit, initial, symmetry
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Exit Mirrors Initial for Symmetry
|
|
8
|
+
|
|
9
|
+
Exit animation should mirror initial for symmetry.
|
|
10
|
+
|
|
11
|
+
**Incorrect (asymmetric exit):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<motion.div
|
|
15
|
+
initial={{ opacity: 0, y: 20 }}
|
|
16
|
+
animate={{ opacity: 1, y: 0 }}
|
|
17
|
+
exit={{ scale: 0 }}
|
|
18
|
+
/>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (symmetric exit):**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
<motion.div
|
|
25
|
+
initial={{ opacity: 0, y: 20 }}
|
|
26
|
+
animate={{ opacity: 1, y: 0 }}
|
|
27
|
+
exit={{ opacity: 0, y: 20 }}
|
|
28
|
+
/>
|
|
29
|
+
```
|