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,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: No Animation for Keyboard Navigation
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: none, keyboard, a11y
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## No Animation for Keyboard Navigation
|
|
8
|
+
|
|
9
|
+
Keyboard navigation should be instant, no animation.
|
|
10
|
+
|
|
11
|
+
**Incorrect (animated focus):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
function Menu() {
|
|
15
|
+
return items.map(item => (
|
|
16
|
+
<motion.li
|
|
17
|
+
whileFocus={{ scale: 1.05 }}
|
|
18
|
+
transition={{ duration: 0.2 }}
|
|
19
|
+
/>
|
|
20
|
+
));
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Correct (CSS focus-visible only):**
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
function Menu() {
|
|
28
|
+
return items.map(item => (
|
|
29
|
+
<li className={styles.menuItem} />
|
|
30
|
+
));
|
|
31
|
+
}
|
|
32
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Click Duration 5-15ms
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: param, click, duration
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Click Duration 5-15ms
|
|
8
|
+
|
|
9
|
+
Click/tap sounds should be 5-15ms duration.
|
|
10
|
+
|
|
11
|
+
**Incorrect (too long):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
const buffer = ctx.createBuffer(1, ctx.sampleRate * 0.1, ctx.sampleRate);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (appropriate duration):**
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
const buffer = ctx.createBuffer(1, ctx.sampleRate * 0.008, ctx.sampleRate);
|
|
21
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Click Filter 3000-6000Hz
|
|
3
|
+
impact: LOW
|
|
4
|
+
tags: param, filter, frequency
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Click Filter 3000-6000Hz
|
|
8
|
+
|
|
9
|
+
Bandpass filter for clicks should be 3000-6000Hz.
|
|
10
|
+
|
|
11
|
+
**Incorrect (too low):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
filter.frequency.value = 500;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (crisp range):**
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
filter.frequency.value = 4000;
|
|
21
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Filter Q Value 2-5
|
|
3
|
+
impact: LOW
|
|
4
|
+
tags: param, q-value, filter
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Filter Q Value 2-5
|
|
8
|
+
|
|
9
|
+
Filter Q for clicks should be 2-5 for focused but not harsh sound.
|
|
10
|
+
|
|
11
|
+
**Incorrect (too resonant):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
filter.Q.value = 15;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (balanced Q):**
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
filter.Q.value = 3;
|
|
21
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Gain Under 1.0
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: param, gain, clipping
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Gain Under 1.0
|
|
8
|
+
|
|
9
|
+
Gain values should not exceed 1.0 to prevent clipping.
|
|
10
|
+
|
|
11
|
+
**Incorrect (clipping):**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
gain.gain.setValueAtTime(1.5, t);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (safe gain):**
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
gain.gain.setValueAtTime(0.3, t);
|
|
21
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Active State Scale Transform
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Missing active state makes interactive elements feel unresponsive and reduces tactile feedback.
|
|
5
|
+
tags: physics, interaction, active-state
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Active State Scale Transform
|
|
9
|
+
|
|
10
|
+
Interactive elements must have active/pressed state with scale transform.
|
|
11
|
+
|
|
12
|
+
**Incorrect (no active state):**
|
|
13
|
+
|
|
14
|
+
```css
|
|
15
|
+
.button:hover { background: var(--gray-3); }
|
|
16
|
+
/* Missing :active state */
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (active state present):**
|
|
20
|
+
|
|
21
|
+
```css
|
|
22
|
+
.button:active { transform: scale(0.98); }
|
|
23
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Stagger Under 50ms Per Item
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Excessive stagger delays list readiness and feels sluggish; keep per-item delay minimal.
|
|
5
|
+
tags: physics, stagger, list-animation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Stagger Under 50ms Per Item
|
|
9
|
+
|
|
10
|
+
Stagger delays must not exceed 50ms per item.
|
|
11
|
+
|
|
12
|
+
**Incorrect (excessive stagger):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
transition={{ staggerChildren: 0.15 }}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (reasonable stagger):**
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
transition={{ staggerChildren: 0.03 }}
|
|
22
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Springs for Overshoot and Settle
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Easing cannot produce natural overshoot; springs are required for organic bounce-and-settle motion.
|
|
5
|
+
tags: physics, spring, overshoot
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Springs for Overshoot and Settle
|
|
9
|
+
|
|
10
|
+
Use springs (not easing) when overshoot-and-settle is needed.
|
|
11
|
+
|
|
12
|
+
**Incorrect (easing for bounce):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
<motion.div transition={{ duration: 0.3, ease: "easeOut" }} />
|
|
16
|
+
// When element should bounce/settle
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (spring physics):**
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<motion.div transition={{ type: "spring", stiffness: 500, damping: 30 }} />
|
|
23
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Subtle Squash and Stretch
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Excessive squash/stretch feels cartoonish and distracts from the interface; subtle deformation adds polish.
|
|
5
|
+
tags: physics, squash-stretch, deformation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Subtle Squash and Stretch
|
|
9
|
+
|
|
10
|
+
Squash/stretch deformation must be subtle (0.95-1.05 range).
|
|
11
|
+
|
|
12
|
+
**Incorrect (excessive deformation):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
<motion.div whileTap={{ scale: 0.8 }} />
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (subtle deformation):**
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<motion.div whileTap={{ scale: 0.98 }} />
|
|
22
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use hitSlop to Trigger Predictions Earlier
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: prefetch, hit-slop, target
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Use hitSlop to Trigger Predictions Earlier
|
|
8
|
+
|
|
9
|
+
Expand the invisible prediction area around elements with hitSlop to start loading sooner.
|
|
10
|
+
|
|
11
|
+
**Incorrect (tight prediction area):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
const { elementRef } = useForesight({
|
|
15
|
+
callback: () => prefetch(),
|
|
16
|
+
hitSlop: 0,
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (expanded prediction area):**
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
const { elementRef } = useForesight({
|
|
24
|
+
callback: () => prefetch(),
|
|
25
|
+
hitSlop: 20,
|
|
26
|
+
});
|
|
27
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prefetch on Keyboard Navigation
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: prefetch, keyboard, tab, a11y
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Prefetch on Keyboard Navigation
|
|
8
|
+
|
|
9
|
+
Monitor focus changes and prefetch when the user is a few tab stops away from a registered element.
|
|
10
|
+
|
|
11
|
+
**Correct (tab-aware prefetching):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
const { elementRef } = useForesight({
|
|
15
|
+
callback: () => router.prefetch("/settings"),
|
|
16
|
+
name: "settings-link",
|
|
17
|
+
// Tab prediction fires when focus approaches
|
|
18
|
+
});
|
|
19
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prefetch by Intent, Not Viewport
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: prefetch, viewport, intent, bundle-size
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Prefetch by Intent, Not Viewport
|
|
8
|
+
|
|
9
|
+
Don't prefetch everything visible in the viewport. Prefetch based on user intent to avoid wasted bandwidth.
|
|
10
|
+
|
|
11
|
+
**Incorrect (prefetch all visible links):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Link href="/page" prefetch={true}>Page</Link>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Correct (intent-based prefetching):**
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
<Link href="/page" prefetch={false}>Page</Link>
|
|
21
|
+
// Let trajectory/hover prediction handle it
|
|
22
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Fall Back Gracefully on Touch Devices
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: prefetch, touch, mobile, fallback
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Fall Back Gracefully on Touch Devices
|
|
8
|
+
|
|
9
|
+
Touch devices have no cursor. Fall back to viewport or touch-start strategies automatically.
|
|
10
|
+
|
|
11
|
+
**Incorrect (assumes cursor exists):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
function PrefetchLink({ href, children }) {
|
|
15
|
+
return (
|
|
16
|
+
<Link
|
|
17
|
+
href={href}
|
|
18
|
+
onMouseMove={() => prefetch(href)}
|
|
19
|
+
>
|
|
20
|
+
{children}
|
|
21
|
+
</Link>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Correct (device-aware strategy):**
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
const { elementRef } = useForesight({
|
|
30
|
+
callback: () => router.prefetch(href),
|
|
31
|
+
hitSlop: 20,
|
|
32
|
+
});
|
|
33
|
+
// Automatically falls back to touch-start on mobile
|
|
34
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Trajectory Prediction Over Hover Prefetching
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: prefetch, trajectory, hover, performance
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Trajectory Prediction Over Hover Prefetching
|
|
8
|
+
|
|
9
|
+
Hover prefetching starts too late. Trajectory prediction fires while the cursor is still in motion, reclaiming 100-200ms.
|
|
10
|
+
|
|
11
|
+
**Incorrect (waits for hover):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Link
|
|
15
|
+
href="/about"
|
|
16
|
+
onMouseEnter={() => router.prefetch("/about")}
|
|
17
|
+
>
|
|
18
|
+
About
|
|
19
|
+
</Link>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (trajectory-based):**
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
const { elementRef } = useForesight({
|
|
26
|
+
callback: () => router.prefetch("/about"),
|
|
27
|
+
hitSlop: 20,
|
|
28
|
+
name: "about-link",
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
<Link ref={elementRef} href="/about">About</Link>
|
|
32
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Predictive Prefetching Selectively
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: prefetch, selective, performance
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Use Predictive Prefetching Selectively
|
|
8
|
+
|
|
9
|
+
Predictive prefetching doesn't belong in every project. Use it where navigation latency is noticeable.
|
|
10
|
+
|
|
11
|
+
**Good use cases:** data-heavy dashboards, multi-page apps with slow API responses, e-commerce product pages.
|
|
12
|
+
|
|
13
|
+
**Bad use cases:** static sites with instant navigation, single-page apps with all data preloaded.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Disable Interactions on Exiting Elements
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: presence, interaction, exit
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Disable Interactions on Exiting Elements
|
|
8
|
+
|
|
9
|
+
Disable interactions on exiting elements using isPresent.
|
|
10
|
+
|
|
11
|
+
**Incorrect (clickable during exit):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
function Card() {
|
|
15
|
+
const isPresent = useIsPresent();
|
|
16
|
+
return <button onClick={handleClick}>Click</button>;
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (disabled during exit):**
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
function Card() {
|
|
24
|
+
const isPresent = useIsPresent();
|
|
25
|
+
return (
|
|
26
|
+
<button onClick={handleClick} disabled={!isPresent}>
|
|
27
|
+
Click
|
|
28
|
+
</button>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: useIsPresent in Child Component
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: presence, hook, component-hierarchy
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## useIsPresent in Child Component
|
|
8
|
+
|
|
9
|
+
useIsPresent must be called from child of AnimatePresence, not parent.
|
|
10
|
+
|
|
11
|
+
**Incorrect (hook in parent):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
function Parent() {
|
|
15
|
+
const isPresent = useIsPresent();
|
|
16
|
+
return (
|
|
17
|
+
<AnimatePresence>
|
|
18
|
+
{show && <Child />}
|
|
19
|
+
</AnimatePresence>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Correct (hook in child):**
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
function Child() {
|
|
28
|
+
const isPresent = useIsPresent();
|
|
29
|
+
return <motion.div data-exiting={!isPresent} />;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Call safeToRemove After Async Work
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: presence, safe-to-remove, async
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Call safeToRemove After Async Work
|
|
8
|
+
|
|
9
|
+
When using usePresence, always call safeToRemove after async work.
|
|
10
|
+
|
|
11
|
+
**Incorrect (missing safeToRemove):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
function AsyncComponent() {
|
|
15
|
+
const [isPresent, safeToRemove] = usePresence();
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!isPresent) {
|
|
19
|
+
cleanup();
|
|
20
|
+
}
|
|
21
|
+
}, [isPresent]);
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Correct (safeToRemove called):**
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
function AsyncComponent() {
|
|
29
|
+
const [isPresent, safeToRemove] = usePresence();
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!isPresent) {
|
|
33
|
+
cleanup().then(safeToRemove);
|
|
34
|
+
}
|
|
35
|
+
}, [isPresent, safeToRemove]);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Content Property Required for Pseudo-Elements
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: pseudo, content, before-after
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Content Property Required for Pseudo-Elements
|
|
8
|
+
|
|
9
|
+
::before and ::after require content property to render.
|
|
10
|
+
|
|
11
|
+
**Incorrect (missing content):**
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
.button::before {
|
|
15
|
+
position: absolute;
|
|
16
|
+
background: var(--gray-3);
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (content set):**
|
|
21
|
+
|
|
22
|
+
```css
|
|
23
|
+
.button::before {
|
|
24
|
+
content: "";
|
|
25
|
+
position: absolute;
|
|
26
|
+
background: var(--gray-3);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use ::first-line for Typographic Treatments
|
|
3
|
+
impact: LOW
|
|
4
|
+
tags: pseudo, first-line, typography
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Use ::first-line for Typographic Treatments
|
|
8
|
+
|
|
9
|
+
Use ::first-line for drop-cap-adjacent styling without JavaScript or hardcoded spans.
|
|
10
|
+
|
|
11
|
+
**Incorrect (manual span):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<p>
|
|
15
|
+
<span className={styles["first-line"]}>The opening line of this paragraph</span>
|
|
16
|
+
is styled differently from the rest.
|
|
17
|
+
</p>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (native ::first-line):**
|
|
21
|
+
|
|
22
|
+
```css
|
|
23
|
+
.article p:first-of-type::first-line {
|
|
24
|
+
font-variant-caps: small-caps;
|
|
25
|
+
font-weight: var(--font-weight-medium);
|
|
26
|
+
}
|
|
27
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Hit Target Expansion with Pseudo-Elements
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: pseudo, hit-target, accessibility
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Hit Target Expansion with Pseudo-Elements
|
|
8
|
+
|
|
9
|
+
Use negative inset values to expand hit targets without extra markup.
|
|
10
|
+
|
|
11
|
+
**Incorrect (wrapper for hit target):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<div className={styles.wrapper}>
|
|
15
|
+
<a className={styles.link}>Link</a>
|
|
16
|
+
</div>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (pseudo-element expansion):**
|
|
20
|
+
|
|
21
|
+
```css
|
|
22
|
+
.link {
|
|
23
|
+
position: relative;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.link::before {
|
|
27
|
+
content: "";
|
|
28
|
+
position: absolute;
|
|
29
|
+
inset: -8px -12px;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use ::marker for Custom List Bullets
|
|
3
|
+
impact: LOW
|
|
4
|
+
tags: pseudo, marker, list, bullets
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Use ::marker for Custom List Bullets
|
|
8
|
+
|
|
9
|
+
Use ::marker to style list bullets without extra elements or background-image hacks.
|
|
10
|
+
|
|
11
|
+
**Incorrect (background image hack):**
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
li {
|
|
15
|
+
list-style: none;
|
|
16
|
+
background: url("bullet.svg") no-repeat 0 4px;
|
|
17
|
+
padding-left: 20px;
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (native ::marker):**
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
li::marker {
|
|
25
|
+
color: var(--gray-8);
|
|
26
|
+
font-size: 0.8em;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Pseudo-Elements Over DOM Nodes
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: pseudo, dom, decorative
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Pseudo-Elements Over DOM Nodes
|
|
8
|
+
|
|
9
|
+
Use pseudo-elements for decorative content instead of extra DOM nodes.
|
|
10
|
+
|
|
11
|
+
**Incorrect (extra DOM node):**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<button className={styles.button}>
|
|
15
|
+
<span className={styles.background} />
|
|
16
|
+
Click me
|
|
17
|
+
</button>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (pseudo-element):**
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
<button className={styles.button}>
|
|
24
|
+
Click me
|
|
25
|
+
</button>
|
|
26
|
+
```
|
|
27
|
+
```css
|
|
28
|
+
.button::before {
|
|
29
|
+
content: "";
|
|
30
|
+
/* decorative background */
|
|
31
|
+
}
|
|
32
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Position Relative Parent for Pseudo-Elements
|
|
3
|
+
impact: HIGH
|
|
4
|
+
tags: pseudo, position, relative
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Position Relative Parent for Pseudo-Elements
|
|
8
|
+
|
|
9
|
+
Parent must have position: relative for absolute pseudo-elements.
|
|
10
|
+
|
|
11
|
+
**Incorrect (no position on parent):**
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
.button::before {
|
|
15
|
+
content: "";
|
|
16
|
+
position: absolute;
|
|
17
|
+
inset: 0;
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (parent positioned):**
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
.button {
|
|
25
|
+
position: relative;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.button::before {
|
|
29
|
+
content: "";
|
|
30
|
+
position: absolute;
|
|
31
|
+
inset: 0;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Z-Index Layering for Pseudo-Elements
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
tags: pseudo, z-index, layering
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Z-Index Layering for Pseudo-Elements
|
|
8
|
+
|
|
9
|
+
Pseudo-elements need z-index to layer correctly with content.
|
|
10
|
+
|
|
11
|
+
**Incorrect (covers text):**
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
.button::before {
|
|
15
|
+
content: "";
|
|
16
|
+
position: absolute;
|
|
17
|
+
inset: 0;
|
|
18
|
+
background: var(--gray-3);
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (layered behind):**
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
.button {
|
|
26
|
+
position: relative;
|
|
27
|
+
z-index: 1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.button::before {
|
|
31
|
+
content: "";
|
|
32
|
+
position: absolute;
|
|
33
|
+
inset: 0;
|
|
34
|
+
background: var(--gray-3);
|
|
35
|
+
z-index: -1;
|
|
36
|
+
}
|
|
37
|
+
```
|