@nerdev/skillset 0.2.1
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/LICENSE +21 -0
- package/README.md +351 -0
- package/bin/skillset.mjs +205 -0
- package/curations/README.md +53 -0
- package/curations/TEMPLATE/WHY.md +23 -0
- package/curations/TEMPLATE/overlay/SKILL.md +6 -0
- package/curations/impeccable/WHY.md +34 -0
- package/curations/impeccable/overlay/SKILL.md +116 -0
- package/package.json +43 -0
- package/scripts/ste-gate.mjs +39 -0
- package/scripts/ste-lint.py +201 -0
- package/scripts/sync.mjs +174 -0
- package/skills/ai-agent/agent-docs-writer/SKILL.md +105 -0
- package/skills/ai-agent/agent-docs-writer/references/principles.md +73 -0
- package/skills/ai-agent/agent-docs-writer/templates/AGENTS.template.md +44 -0
- package/skills/ai-agent/agent-docs-writer/templates/README.template.md +19 -0
- package/skills/ai-agent/ultra-context-engine/SKILL.md +941 -0
- package/skills/architecture/blueprint/SKILL.md +191 -0
- package/skills/architecture/code-tour/SKILL.md +204 -0
- package/skills/architecture/progress-guard/SKILL.md +148 -0
- package/skills/architecture/search-first/SKILL.md +97 -0
- package/skills/backend/api-design/SKILL.md +456 -0
- package/skills/backend/auth/SKILL.md +596 -0
- package/skills/backend/database/SKILL.md +431 -0
- package/skills/backend/realtime/SKILL.md +670 -0
- package/skills/backend/security/SKILL.md +590 -0
- package/skills/business/brag/SKILL.md +177 -0
- package/skills/business/brag/assets/music/README.md +20 -0
- package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.json +3846 -0
- package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.md +35 -0
- package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.json +1826 -0
- package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.md +35 -0
- package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.json +2266 -0
- package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.md +35 -0
- package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.json +2806 -0
- package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.md +35 -0
- package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.json +2796 -0
- package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.md +35 -0
- package/skills/business/brag/assets/music/happy-beats-business-moves-vol-1-by-ende-dot-app.mp3 +0 -0
- package/skills/business/brag/assets/music/happy-beats-business-moves-vol-10-by-ende-dot-app.mp3 +0 -0
- package/skills/business/brag/assets/music/happy-beats-business-moves-vol-11-by-ende-dot-app.mp3 +0 -0
- package/skills/business/brag/assets/music/happy-beats-business-moves-vol-12-by-ende-dot-app.mp3 +0 -0
- package/skills/business/brag/assets/music/happy-beats-business-moves-vol-9-by-ende-dot-app.mp3 +0 -0
- package/skills/business/brag/assets/sfx/casino/card-fan-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-fan-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-place-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-place-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-place-3.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-place-4.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-shove-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-shove-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-shove-3.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-shove-4.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-shuffle.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-slide-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-slide-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-slide-3.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-slide-4.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-slide-5.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-slide-6.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-slide-7.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/card-slide-8.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/cards-pack-open-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/cards-pack-open-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chip-lay-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chip-lay-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chip-lay-3.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-collide-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-collide-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-collide-3.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-collide-4.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-handle-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-handle-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-handle-3.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-handle-4.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-handle-6.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-stack-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-stack-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-stack-3.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-stack-4.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-stack-5.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/chips-stack-6.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/dice-grab-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/dice-grab-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/dice-shake-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/dice-shake-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/dice-shake-3.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/dice-throw-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/dice-throw-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/dice-throw-3.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/die-throw-1.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/die-throw-2.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/die-throw-3.ogg +0 -0
- package/skills/business/brag/assets/sfx/casino/die-throw-4.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_carpet_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_carpet_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_carpet_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_concrete_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_concrete_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_concrete_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_concrete_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_concrete_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_grass_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_grass_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_grass_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_grass_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_grass_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_snow_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_snow_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_snow_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_snow_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_snow_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_wood_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_wood_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_wood_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_wood_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/footstep_wood_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactBell_heavy_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactBell_heavy_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactBell_heavy_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGeneric_light_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGeneric_light_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGeneric_light_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGeneric_light_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGeneric_light_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGlass_heavy_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGlass_light_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGlass_light_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGlass_light_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGlass_medium_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGlass_medium_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactGlass_medium_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactMetal_heavy_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactMetal_heavy_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactMetal_heavy_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactMetal_light_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactMetal_light_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactMetal_medium_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactMetal_medium_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactMetal_medium_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactMetal_medium_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactMetal_medium_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactMining_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlank_medium_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlank_medium_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlank_medium_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlank_medium_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlank_medium_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_heavy_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_heavy_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_heavy_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_heavy_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_heavy_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_light_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_light_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_light_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_light_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_light_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_medium_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_medium_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_medium_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_medium_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPlate_medium_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPunch_heavy_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPunch_heavy_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPunch_heavy_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPunch_heavy_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPunch_heavy_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPunch_medium_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPunch_medium_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPunch_medium_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPunch_medium_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactPunch_medium_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactSoft_heavy_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactSoft_heavy_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactSoft_heavy_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactSoft_heavy_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactSoft_heavy_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactSoft_medium_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactSoft_medium_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactSoft_medium_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactSoft_medium_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactSoft_medium_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactTin_medium_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactTin_medium_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactTin_medium_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactTin_medium_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactTin_medium_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_heavy_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_heavy_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_heavy_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_heavy_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_heavy_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_light_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_light_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_light_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_light_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_light_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_medium_000.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_medium_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_medium_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_medium_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/impact/impactWood_medium_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/bong_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/click_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/click_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/click_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/click_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/click_005.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/drop_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/drop_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/drop_003.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/error_005.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/error_006.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/glitch_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/glitch_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/select_008.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/switch_001.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/switch_002.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/switch_004.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/switch_005.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/switch_006.ogg +0 -0
- package/skills/business/brag/assets/sfx/interface/switch_007.ogg +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-001.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-002.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-003.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-004.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-005.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-006.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-007.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-008.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-009.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-010.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-011.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-012.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-013.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-014.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-015.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-016.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-017.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-018.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-019.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-020.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-021.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-022.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-023.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-024.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-025.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-026.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-027.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-028.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-029.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-030.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-031.wav +0 -0
- package/skills/business/brag/assets/sfx/keyboard/keypress-032.wav +0 -0
- package/skills/business/brag/assets/sfx/sfx-analysis.json +15584 -0
- package/skills/business/brag/assets/sfx/sfx-analysis.md +75 -0
- package/skills/business/brag/assets/sfx/ui/click1.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/click2.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/click3.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/click4.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/click5.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/mouseclick1.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/rollover1.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/rollover2.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/rollover4.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/rollover5.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch1.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch10.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch11.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch12.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch13.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch14.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch15.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch16.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch17.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch18.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch19.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch2.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch20.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch21.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch22.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch23.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch24.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch25.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch26.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch27.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch28.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch29.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch3.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch30.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch31.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch32.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch33.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch34.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch35.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch36.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch37.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch38.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch4.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch5.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch6.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch7.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch8.ogg +0 -0
- package/skills/business/brag/assets/sfx/ui/switch9.ogg +0 -0
- package/skills/business/brag/references/audio.md +307 -0
- package/skills/business/brag/references/step-1-inspect.md +133 -0
- package/skills/business/brag/references/step-2-plan.md +234 -0
- package/skills/business/brag/references/step-3-compose.md +210 -0
- package/skills/business/brag/references/step-4-deliver.md +168 -0
- package/skills/business/brag/references/tones.md +231 -0
- package/skills/business/brag/scripts/analyze_music_cues.py +309 -0
- package/skills/business/brag/scripts/pyproject.toml +14 -0
- package/skills/business/brag/scripts/uv.lock +819 -0
- package/skills/business/copywriting/SKILL.md +178 -0
- package/skills/business/customer-research/SKILL.md +259 -0
- package/skills/business/freelancing/SKILL.md +679 -0
- package/skills/business/launch/SKILL.md +273 -0
- package/skills/business/marketing-psychology/SKILL.md +364 -0
- package/skills/business/pricing/SKILL.md +233 -0
- package/skills/business/resume-revamp-jake-ats/SKILL.md +64 -0
- package/skills/business/startup-founder/SKILL.md +400 -0
- package/skills/business/startup-founder/references/funding.md +48 -0
- package/skills/business/startup-founder/references/further-reading.md +132 -0
- package/skills/business/startup-founder/references/gtm-tools.md +57 -0
- package/skills/business/startup-founder/references/legal-india.md +36 -0
- package/skills/business/startup-founder/references/marketing/README.md +16 -0
- package/skills/business/startup-founder/references/marketing/cold-email.md +172 -0
- package/skills/business/startup-founder/references/marketing/competitors.md +245 -0
- package/skills/business/startup-founder/references/marketing/customer-research.md +276 -0
- package/skills/business/startup-founder/references/marketing/free-tools.md +168 -0
- package/skills/business/startup-founder/references/marketing/launch.md +343 -0
- package/skills/business/startup-founder/references/marketing/marketing-plan.md +270 -0
- package/skills/business/startup-founder/references/marketing/pricing.md +237 -0
- package/skills/business/startup-founder/references/marketing/product-marketing.md +250 -0
- package/skills/business/startup-founder/references/validation.md +267 -0
- package/skills/devops/deployments/SKILL.md +339 -0
- package/skills/devops/git-ci/SKILL.md +372 -0
- package/skills/devops/performance/SKILL.md +523 -0
- package/skills/devops/testing/SKILL.md +558 -0
- package/skills/engineering/code-review/SKILL.md +149 -0
- package/skills/engineering/coding-standards/SKILL.md +229 -0
- package/skills/engineering/product-engineering/SKILL.md +219 -0
- package/skills/engineering/senior-engineer-prompts/SKILL.md +145 -0
- package/skills/engineering/tdd-workflow/SKILL.md +178 -0
- package/skills/engineering/verification-loop/SKILL.md +199 -0
- package/skills/nerdev/nerdev-abstraction/SKILL.md +412 -0
- package/skills/nerdev/nerdev-docs/SKILL.md +527 -0
- package/skills/nerdev/nerdev-docs/templates/adr-template.md +39 -0
- package/skills/nerdev/nerdev-docs/templates/contributing-template.md +140 -0
- package/skills/nerdev/nerdev-docs/templates/deploy-template.md +426 -0
- package/skills/nerdev/nerdev-docs/templates/design-template.md +105 -0
- package/skills/nerdev/nerdev-docs/templates/incident-template.md +66 -0
- package/skills/nerdev/nerdev-monorepo/SKILL.md +522 -0
- package/skills/quality/output-skill/SKILL.md +49 -0
- package/skills/quality/unslop/SKILL.md +67 -0
- package/skills/ui/animaxxing/SKILL.md +58 -0
- package/skills/ui/animaxxing/agents/openai.yaml +7 -0
- package/skills/ui/animaxxing/references/effect-restoration.md +26 -0
- package/skills/ui/animaxxing/references/motion-vocabulary.md +142 -0
- package/skills/ui/animaxxing/references/recipes/blast-off.md +119 -0
- package/skills/ui/animaxxing/references/recipes/particle-effects.md +718 -0
- package/skills/ui/animaxxing/references/recipes/particle-field.md +428 -0
- package/skills/ui/animaxxing/references/recipes/route-letters.md +205 -0
- package/skills/ui/animaxxing/references/recipes/speak-in.md +181 -0
- package/skills/ui/animaxxing/references/recipes/split-entrances.md +345 -0
- package/skills/ui/animaxxing/references/recipes/wave.md +144 -0
- package/skills/ui/animaxxing/references/text-stability.md +68 -0
- package/skills/ui/animaxxing/references/verification.md +44 -0
- package/skills/ui/ask-animation/SKILL.md +178 -0
- package/skills/ui/ask-base-ui/SKILL.md +400 -0
- package/skills/ui/ask-cmdk/SKILL.md +315 -0
- package/skills/ui/ask-command-menu/SKILL.md +262 -0
- package/skills/ui/ask-drawer/SKILL.md +235 -0
- package/skills/ui/ask-form/SKILL.md +229 -0
- package/skills/ui/ask-gsap/SKILL.md +435 -0
- package/skills/ui/ask-modal/SKILL.md +145 -0
- package/skills/ui/ask-motion/SKILL.md +339 -0
- package/skills/ui/ask-rhf/SKILL.md +388 -0
- package/skills/ui/ask-sonner/API.md +64 -0
- package/skills/ui/ask-sonner/SKILL.md +88 -0
- package/skills/ui/ask-table/SKILL.md +253 -0
- package/skills/ui/ask-tanstack-table/SKILL.md +490 -0
- package/skills/ui/ask-toast/SKILL.md +163 -0
- package/skills/ui/ask-vaul/SKILL.md +291 -0
- package/skills/ui/ask-virtual-list/SKILL.md +304 -0
- package/skills/ui/ask-virtuoso/SKILL.md +332 -0
- package/skills/ui/email-craft/SKILL.md +258 -0
- package/skills/ui/frontend-craftsmanship/SKILL.md +462 -0
- package/skills/ui/gsap-astro/SKILL.md +56 -0
- package/skills/ui/gsap-astro/agents/openai.yaml +7 -0
- package/skills/ui/gsap-astro/references/client-router-navigation.md +139 -0
- package/skills/ui/gsap-astro/references/initialization.md +87 -0
- package/skills/ui/gsap-astro/references/motion-system.md +152 -0
- package/skills/ui/gsap-astro/references/scripts-and-islands.md +65 -0
- package/skills/ui/gsap-astro/references/verification.md +44 -0
- package/skills/ui/gsap-core/SKILL.md +254 -0
- package/skills/ui/gsap-frameworks/SKILL.md +266 -0
- package/skills/ui/gsap-nextjs/SKILL.md +51 -0
- package/skills/ui/gsap-nextjs/agents/openai.yaml +7 -0
- package/skills/ui/gsap-nextjs/references/app-router-navigation.md +189 -0
- package/skills/ui/gsap-nextjs/references/initialization.md +87 -0
- package/skills/ui/gsap-nextjs/references/motion-system.md +158 -0
- package/skills/ui/gsap-nextjs/references/verification.md +40 -0
- package/skills/ui/gsap-nuxt/SKILL.md +56 -0
- package/skills/ui/gsap-nuxt/agents/openai.yaml +7 -0
- package/skills/ui/gsap-nuxt/references/initialization.md +87 -0
- package/skills/ui/gsap-nuxt/references/motion-system.md +161 -0
- package/skills/ui/gsap-nuxt/references/navigation.md +145 -0
- package/skills/ui/gsap-nuxt/references/page-transitions.md +107 -0
- package/skills/ui/gsap-nuxt/references/verification.md +42 -0
- package/skills/ui/gsap-performance/SKILL.md +79 -0
- package/skills/ui/gsap-plugins/SKILL.md +433 -0
- package/skills/ui/gsap-react/SKILL.md +136 -0
- package/skills/ui/gsap-react-router/SKILL.md +55 -0
- package/skills/ui/gsap-react-router/agents/openai.yaml +7 -0
- package/skills/ui/gsap-react-router/references/initialization.md +87 -0
- package/skills/ui/gsap-react-router/references/motion-system.md +158 -0
- package/skills/ui/gsap-react-router/references/react-router-navigation.md +165 -0
- package/skills/ui/gsap-react-router/references/route-lifetime.md +74 -0
- package/skills/ui/gsap-react-router/references/verification.md +42 -0
- package/skills/ui/gsap-scrolltrigger/SKILL.md +296 -0
- package/skills/ui/gsap-sveltekit/SKILL.md +54 -0
- package/skills/ui/gsap-sveltekit/agents/openai.yaml +7 -0
- package/skills/ui/gsap-sveltekit/references/initialization.md +87 -0
- package/skills/ui/gsap-sveltekit/references/motion-system.md +156 -0
- package/skills/ui/gsap-sveltekit/references/page-lifetime.md +63 -0
- package/skills/ui/gsap-sveltekit/references/sveltekit-navigation.md +160 -0
- package/skills/ui/gsap-sveltekit/references/verification.md +41 -0
- package/skills/ui/gsap-tanstack-router/SKILL.md +56 -0
- package/skills/ui/gsap-tanstack-router/agents/openai.yaml +7 -0
- package/skills/ui/gsap-tanstack-router/references/initialization.md +87 -0
- package/skills/ui/gsap-tanstack-router/references/motion-system.md +160 -0
- package/skills/ui/gsap-tanstack-router/references/route-lifetime.md +66 -0
- package/skills/ui/gsap-tanstack-router/references/tanstack-navigation.md +156 -0
- package/skills/ui/gsap-tanstack-router/references/verification.md +41 -0
- package/skills/ui/gsap-timeline/SKILL.md +107 -0
- package/skills/ui/gsap-utils/SKILL.md +284 -0
- package/skills/ui/gsap-vanilla/SKILL.md +63 -0
- package/skills/ui/gsap-vanilla/agents/openai.yaml +7 -0
- package/skills/ui/gsap-vanilla/references/cross-document-navigation.md +54 -0
- package/skills/ui/gsap-vanilla/references/initialization.md +87 -0
- package/skills/ui/gsap-vanilla/references/motion-system.md +150 -0
- package/skills/ui/gsap-vanilla/references/page-load.md +96 -0
- package/skills/ui/gsap-vanilla/references/spa-navigation.md +70 -0
- package/skills/ui/gsap-vanilla/references/verification.md +41 -0
- package/skills/ui/house-style/SKILL.md +366 -0
- package/skills/ui/imagegen/SKILL.md +49 -0
- package/skills/ui/imagegen/references/brandkit.md +48 -0
- package/skills/ui/imagegen/references/image-to-code.md +56 -0
- package/skills/ui/imagegen/references/mobile.md +58 -0
- package/skills/ui/imagegen/references/web.md +63 -0
- package/skills/ui/impeccable/SKILL.md +116 -0
- package/skills/ui/impeccable/reference/adapt.md +318 -0
- package/skills/ui/impeccable/reference/adapt.native.md +58 -0
- package/skills/ui/impeccable/reference/android.md +46 -0
- package/skills/ui/impeccable/reference/animate.md +89 -0
- package/skills/ui/impeccable/reference/audit.md +137 -0
- package/skills/ui/impeccable/reference/audit.native.md +139 -0
- package/skills/ui/impeccable/reference/bolder.md +33 -0
- package/skills/ui/impeccable/reference/clarify.md +94 -0
- package/skills/ui/impeccable/reference/colorize.md +86 -0
- package/skills/ui/impeccable/reference/craft-floor.md +44 -0
- package/skills/ui/impeccable/reference/craft.md +5 -0
- package/skills/ui/impeccable/reference/critique.md +806 -0
- package/skills/ui/impeccable/reference/degraded/asset-producer.md +37 -0
- package/skills/ui/impeccable/reference/degraded/documenter.md +24 -0
- package/skills/ui/impeccable/reference/degraded/finish-reviewer.md +38 -0
- package/skills/ui/impeccable/reference/degraded/manual-edit-applier.md +92 -0
- package/skills/ui/impeccable/reference/delight.md +70 -0
- package/skills/ui/impeccable/reference/distill.md +111 -0
- package/skills/ui/impeccable/reference/doctor.md +54 -0
- package/skills/ui/impeccable/reference/document.md +416 -0
- package/skills/ui/impeccable/reference/extract.md +69 -0
- package/skills/ui/impeccable/reference/generate.md +101 -0
- package/skills/ui/impeccable/reference/harden.md +345 -0
- package/skills/ui/impeccable/reference/hooks.md +111 -0
- package/skills/ui/impeccable/reference/init.md +131 -0
- package/skills/ui/impeccable/reference/ios.md +51 -0
- package/skills/ui/impeccable/reference/layout.md +84 -0
- package/skills/ui/impeccable/reference/live-setup.md +104 -0
- package/skills/ui/impeccable/reference/live.md +325 -0
- package/skills/ui/impeccable/reference/new-work.md +145 -0
- package/skills/ui/impeccable/reference/onboard.md +234 -0
- package/skills/ui/impeccable/reference/operate.md +61 -0
- package/skills/ui/impeccable/reference/optimize.md +258 -0
- package/skills/ui/impeccable/reference/overdrive.md +127 -0
- package/skills/ui/impeccable/reference/polish.md +105 -0
- package/skills/ui/impeccable/reference/quieter.md +99 -0
- package/skills/ui/impeccable/reference/routing.md +24 -0
- package/skills/ui/impeccable/reference/shape.md +59 -0
- package/skills/ui/impeccable/reference/typeset.md +80 -0
- package/skills/ui/impeccable/reference/visualize.md +46 -0
- package/skills/ui/impeccable/scripts/VERSION +1 -0
- package/skills/ui/impeccable/scripts/command-metadata.json +98 -0
- package/skills/ui/impeccable/scripts/data/font-index-failures.json +121 -0
- package/skills/ui/impeccable/scripts/data/font-index.json +1 -0
- package/skills/ui/impeccable/scripts/impeccable +206 -0
- package/skills/ui/impeccable/scripts/impeccable.cmd +214 -0
- package/skills/ui/impeccable/scripts/live-browser-dom.js +167 -0
- package/skills/ui/impeccable/scripts/live-browser-ignores.js +242 -0
- package/skills/ui/impeccable/scripts/live-browser-session.js +144 -0
- package/skills/ui/impeccable/scripts/live-browser.js +13510 -0
- package/skills/ui/impeccable/scripts/modern-screenshot.umd.js +14 -0
- package/skills/ui/motion/SKILL.md +207 -0
- package/skills/ui/pick-ui-library/SKILL.md +85 -0
- package/skills/ui/product-messaging/SKILL.md +163 -0
- package/skills/ui/product-site/SKILL.md +465 -0
- package/skills/ui/prototype/PICKER.md +197 -0
- package/skills/ui/prototype/SKILL.md +98 -0
- package/skills/ui/refero-design/SKILL.md +534 -0
- package/skills/ui/refero-design/agents/openai.yaml +4 -0
- package/skills/ui/refero-design/references/anti-ai-slop.md +292 -0
- package/skills/ui/refero-design/references/color.md +563 -0
- package/skills/ui/refero-design/references/copywriting.md +222 -0
- package/skills/ui/refero-design/references/craft-details.md +525 -0
- package/skills/ui/refero-design/references/example-workflow.md +264 -0
- package/skills/ui/refero-design/references/icons.md +166 -0
- package/skills/ui/refero-design/references/mcp-tools.md +258 -0
- package/skills/ui/refero-design/references/motion.md +504 -0
- package/skills/ui/refero-design/references/typography.md +737 -0
- package/skills/ui/refero-design/references/visual-workflow.md +105 -0
- package/skills/ui/style-animaxxing/SKILL.md +48 -0
- package/skills/ui/style-animaxxing/agents/openai.yaml +7 -0
- package/skills/ui/style-animaxxing/references/motion-vocabulary.md +51 -0
- package/skills/ui/style-animaxxing/references/tokens.md +288 -0
- package/skills/ui/style-animaxxing/references/typography-and-layout.md +149 -0
- package/skills/ui/style-animaxxing/references/verification.md +38 -0
- package/skills/ui/typography-picker/SKILL.md +305 -0
- package/skills/ui/ui-core/SKILL.md +345 -0
- package/skills/ui/ui-core/references/accessibility.md +85 -0
- package/skills/ui/ui-core/references/build.md +666 -0
- package/skills/ui/ui-core/references/copy.md +70 -0
- package/skills/ui/ui-core/references/dark-mode.md +76 -0
- package/skills/ui/ui-core/references/direction.md +99 -0
- package/skills/ui/ui-core/references/redesign.md +74 -0
- package/skills/ui/ui-core/references/responsive.md +77 -0
- package/skills/ui/ui-core/references/styles.md +42 -0
- package/skills/ui/ui-core/references/system.md +190 -0
- package/skills/ui/ui-core/references/verify.md +37 -0
- package/skills/ui/ui-engineering/SKILL.md +436 -0
- package/skills/ui/ui-engineering/references/avatars.md +74 -0
- package/skills/ui/ui-engineering/references/card-patterns.md +66 -0
- package/skills/ui/ui-engineering/references/floating-actions.md +65 -0
- package/skills/ui/ui-engineering/references/navigation.md +81 -0
- package/skills/ui/ui-engineering/references/overlays.md +74 -0
- package/skills/ui/ui-engineering/references/pagination.md +71 -0
- package/skills/ui/ui-engineering/references/progress-indicators.md +77 -0
- package/skills/ui/ui-engineering/references/quality-bar.md +293 -0
- package/skills/ui/ui-engineering/references/search.md +68 -0
- package/skills/ui/ui-engineering/references/tailwind-architecture.md +81 -0
- package/skills/ui/ui-theme-picker/SKILL.md +522 -0
- package/skills/ui/ui-theme-picker/references/component-sources.md +223 -0
- package/skills/ui/ui-theme-picker/references/styles.md +298 -0
- package/skills/ui/vitepress-docs/SKILL.md +439 -0
- package/skills/ui/vitepress-docs/references/components.md +333 -0
- package/skills/ui/vitepress-docs/references/layout.md +128 -0
- package/skills/ui/vitepress-docs/references/tokens.md +87 -0
- package/skills/ui/vitepress-docs/references/typography.md +76 -0
- package/skills/workflow/careful/SKILL.md +48 -0
- package/skills/workflow/context-restore/SKILL.md +69 -0
- package/skills/workflow/context-save/SKILL.md +96 -0
- package/skills/workflow/health/SKILL.md +99 -0
- package/skills/workflow/learn/SKILL.md +95 -0
- package/skills/workflow/office-hours/SKILL.md +136 -0
- package/skills/workflow/ship/SKILL.md +95 -0
- package/vendor.json +80 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ask-virtuoso
|
|
3
|
+
description: Library guide for Virtuoso: virtualized lists with variable heights, grouping, infinite scroll, smooth scrolling. Load via ask-virtual-list pattern skill.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ask Virtuoso: Virtualized Lists
|
|
7
|
+
|
|
8
|
+
**When to use:** Long lists: feeds, chat, logs, activity, notifications, search results. Handles variable heights natively.
|
|
9
|
+
|
|
10
|
+
**Package:** `react-virtuoso`
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm i react-virtuoso
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Core Components
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso'
|
|
24
|
+
import { useInView } from 'react-intersection-observer'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## Basic List
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { Virtuoso } from 'react-virtuoso'
|
|
32
|
+
|
|
33
|
+
function SimpleList({ items }: { items: Item[] }) {
|
|
34
|
+
return (
|
|
35
|
+
<Virtuoso
|
|
36
|
+
data={items}
|
|
37
|
+
itemContent={(index, item) => <ItemComponent item={item} />}
|
|
38
|
+
overscan={100}
|
|
39
|
+
style={{ height: '100%', width: '100%' }}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## Infinite Scroll (Feed)
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { useState, useCallback } from 'react'
|
|
50
|
+
import { Virtuoso } from 'react-virtuoso'
|
|
51
|
+
import { useInView } from 'react-intersection-observer'
|
|
52
|
+
|
|
53
|
+
function InfiniteFeed() {
|
|
54
|
+
const [items, setItems] = useState<FeedItem[]>([])
|
|
55
|
+
const [page, setPage] = useState(1)
|
|
56
|
+
const [hasMore, setHasMore] = useState(true)
|
|
57
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
58
|
+
|
|
59
|
+
const { ref: loadMoreRef, inView } = useInView({ threshold: 0.1 })
|
|
60
|
+
|
|
61
|
+
const loadMore = useCallback(async () => {
|
|
62
|
+
if (isLoading || !hasMore) return
|
|
63
|
+
setIsLoading(true)
|
|
64
|
+
try {
|
|
65
|
+
const { data, hasMore: more } = await api.getFeed({ page: page + 1 })
|
|
66
|
+
setItems(prev => [...prev, ...data])
|
|
67
|
+
setPage(p => p + 1)
|
|
68
|
+
setHasMore(more)
|
|
69
|
+
} finally {
|
|
70
|
+
setIsLoading(false)
|
|
71
|
+
}
|
|
72
|
+
}, [page, hasMore, isLoading])
|
|
73
|
+
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (inView && hasMore && !isLoading) loadMore()
|
|
76
|
+
}, [inView, hasMore, isLoading, loadMore])
|
|
77
|
+
|
|
78
|
+
// Initial load
|
|
79
|
+
useEffect(() => { loadMore() }, [loadMore])
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<Virtuoso
|
|
83
|
+
data={items}
|
|
84
|
+
itemContent={(index, item) => <FeedItemComponent item={item} />}
|
|
85
|
+
endContent={
|
|
86
|
+
hasMore ? (
|
|
87
|
+
<div className="flex justify-center py-4" ref={loadMoreRef}>
|
|
88
|
+
{isLoading ? <Spinner className="h-6 w-6" /> : null}
|
|
89
|
+
</div>
|
|
90
|
+
) : (
|
|
91
|
+
<div className="flex justify-center py-4 text-gray-500">All caught up!</div>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
overscan={50}
|
|
95
|
+
style={{ height: '100%', width: '100%' }}
|
|
96
|
+
/>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
## Skeletons (Match Real Height)
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
function FeedSkeleton() {
|
|
106
|
+
return (
|
|
107
|
+
<div className="p-4 border-b dark:border-gray-800 animate-pulse">
|
|
108
|
+
<div className="flex gap-3">
|
|
109
|
+
<div className="h-10 w-10 rounded-full bg-gray-200 dark:bg-gray-700" />
|
|
110
|
+
<div className="flex-1 space-y-2">
|
|
111
|
+
<div className="h-4 w-3/4 bg-gray-200 dark:bg-gray-700 rounded" />
|
|
112
|
+
<div className="h-3 w-1/2 bg-gray-200 dark:bg-gray-700 rounded" />
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// In Virtuoso
|
|
120
|
+
<Virtuoso
|
|
121
|
+
data={items}
|
|
122
|
+
itemContent={(index, item) => <FeedItem item={item} />}
|
|
123
|
+
endContent={hasMore ? <LoadMoreTrigger /> : <EndMessage />}
|
|
124
|
+
/>
|
|
125
|
+
|
|
126
|
+
// Custom endContent with skeleton while loading
|
|
127
|
+
function LoadMoreTrigger({ isLoading, ref }) {
|
|
128
|
+
return (
|
|
129
|
+
<div ref={ref} className="flex justify-center py-4">
|
|
130
|
+
{isLoading ? <FeedSkeleton /> : null}
|
|
131
|
+
</div>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
## Grouped Lists (Sticky Headers)
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
import { GroupedVirtuoso } from 'react-virtuoso'
|
|
141
|
+
|
|
142
|
+
function GroupedList({ groups }: { groups: Group[] }) {
|
|
143
|
+
return (
|
|
144
|
+
<GroupedVirtuoso
|
|
145
|
+
groupCounts={groups.map(g => g.items.length)}
|
|
146
|
+
groupContent={({ index }) => (
|
|
147
|
+
<div className="sticky top-0 z-10 px-4 py-2 bg-white dark:bg-gray-900 border-b font-semibold">
|
|
148
|
+
{groups[index].title}
|
|
149
|
+
</div>
|
|
150
|
+
)}
|
|
151
|
+
itemContent={(index, item) => <ItemComponent item={item} />}
|
|
152
|
+
overscan={20}
|
|
153
|
+
style={{ height: '100%', width: '100%' }}
|
|
154
|
+
/>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
## Chat / Messages (Variable Heights, Auto-scroll)
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import { useRef, useEffect, useState } from 'react'
|
|
164
|
+
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso'
|
|
165
|
+
|
|
166
|
+
function ChatList({ messages }: { messages: Message[] }) {
|
|
167
|
+
const virtuosoRef = useRef<VirtuosoHandle>(null)
|
|
168
|
+
const [atBottom, setAtBottom] = useState(true)
|
|
169
|
+
|
|
170
|
+
const handleScroll = useCallback((top: number) => {
|
|
171
|
+
const el = virtuosoRef.current?.getScrollElement()
|
|
172
|
+
if (!el) return
|
|
173
|
+
const { scrollHeight, clientHeight, scrollTop } = el
|
|
174
|
+
setAtBottom(scrollHeight - scrollTop - clientHeight < 100)
|
|
175
|
+
}, [])
|
|
176
|
+
|
|
177
|
+
// Auto-scroll on new messages if at bottom
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
if (atBottom && messages.length > 0) {
|
|
180
|
+
virtuosoRef.current?.scrollToIndex({ index: messages.length - 1, align: 'end', behavior: 'smooth' })
|
|
181
|
+
}
|
|
182
|
+
}, [messages, atBottom])
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<Virtuoso
|
|
186
|
+
ref={virtuosoRef}
|
|
187
|
+
data={messages}
|
|
188
|
+
itemContent={(index, message) => <MessageBubble message={message} />}
|
|
189
|
+
overscan={20}
|
|
190
|
+
style={{ height: '100%', width: '100%' }}
|
|
191
|
+
onScroll={handleScroll}
|
|
192
|
+
endContent={
|
|
193
|
+
!atBottom && (
|
|
194
|
+
<button
|
|
195
|
+
className="fixed bottom-4 right-4 z-10 px-3 py-1 rounded-full bg-primary text-primary-foreground shadow-lg"
|
|
196
|
+
onClick={() => virtuosoRef.current?.scrollToIndex({ index: messages.length - 1, align: 'end' })}
|
|
197
|
+
>
|
|
198
|
+
↓ {messages.length - virtuosoRef.current?.getFirstVisibleIndex() ?? 0} new
|
|
199
|
+
</button>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
/>
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
## Scroll Restoration (Back Navigation)
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
import { useEffect, useRef } from 'react'
|
|
212
|
+
|
|
213
|
+
function useScrollRestoration(key: string) {
|
|
214
|
+
const saved = useRef<number | null>(null)
|
|
215
|
+
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
return () => {
|
|
218
|
+
const el = document.querySelector('[data-virtuoso-scroller]')
|
|
219
|
+
if (el) saved.current = el.scrollTop
|
|
220
|
+
sessionStorage.setItem(`scroll:${key}`, String(saved.current ?? 0))
|
|
221
|
+
}
|
|
222
|
+
}, [key])
|
|
223
|
+
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
const stored = sessionStorage.getItem(`scroll:${key}`)
|
|
226
|
+
if (stored) {
|
|
227
|
+
const el = document.querySelector('[data-virtuoso-scroller]')
|
|
228
|
+
if (el) el.scrollTop = Number(stored)
|
|
229
|
+
}
|
|
230
|
+
}, [key])
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Usage
|
|
234
|
+
function FeedPage() {
|
|
235
|
+
useScrollRestoration('feed')
|
|
236
|
+
return <InfiniteFeed />
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
## Jump to Index / Top / Bottom
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
const virtuosoRef = useRef<VirtuosoHandle>(null)
|
|
245
|
+
|
|
246
|
+
function JumpButtons() {
|
|
247
|
+
return (
|
|
248
|
+
<div className="fixed bottom-4 right-4 flex flex-col gap-2">
|
|
249
|
+
<Button onClick={() => virtuosoRef.current?.scrollToIndex({ index: 0, align: 'start' })}>
|
|
250
|
+
Top
|
|
251
|
+
</Button>
|
|
252
|
+
<Button onClick={() => virtuosoRef.current?.scrollToIndex({ index: data.length - 1, align: 'end' })}>
|
|
253
|
+
Bottom
|
|
254
|
+
</Button>
|
|
255
|
+
</div>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
## Reverse List (Chat - Newest at Bottom)
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
<Virtuoso
|
|
265
|
+
data={messages}
|
|
266
|
+
itemContent={(index, message) => <MessageBubble message={message} />}
|
|
267
|
+
reverse={true} // Newest at bottom
|
|
268
|
+
overscan={20}
|
|
269
|
+
style={{ height: '100%', width: '100%' }}
|
|
270
|
+
/>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
## Fixed Height Items (Performance)
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
// If ALL items have fixed height, use fixed size for max performance
|
|
278
|
+
<Virtuoso
|
|
279
|
+
data={items}
|
|
280
|
+
itemContent={(index, item) => <Item item={item} />}
|
|
281
|
+
fixedItemSize={48} // Skip measuring
|
|
282
|
+
overscan={10}
|
|
283
|
+
style={{ height: '100%', width: '100%' }}
|
|
284
|
+
/>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
## Common Props Reference
|
|
289
|
+
|
|
290
|
+
| Prop | Type | Description |
|
|
291
|
+
|------|------|-------------|
|
|
292
|
+
| `data` | `T[]` | Array of items |
|
|
293
|
+
| `itemContent` | `(index, item) => ReactNode` | Render function |
|
|
294
|
+
| `overscan` | `number` | Items to render outside viewport (default 5) |
|
|
295
|
+
| `fixedItemSize` | `number` | Skip measuring if fixed height |
|
|
296
|
+
| `reverse` | `boolean` | Render from bottom |
|
|
297
|
+
| `endContent` | `ReactNode` | Loading/end message |
|
|
298
|
+
| `onScroll` | `(top: number) => void` | Scroll callback |
|
|
299
|
+
| `style` | `CSSProperties` | Container style (must have height) |
|
|
300
|
+
| `listRef` | `Ref<VirtuosoHandle>` | Access scroll methods |
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
## VirtuosoHandle Methods
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
const ref = useRef<VirtuosoHandle>(null)
|
|
307
|
+
|
|
308
|
+
ref.current?.scrollToIndex({ index: 10, align: 'center', behavior: 'smooth' })
|
|
309
|
+
ref.current?.scrollToTop({ behavior: 'smooth' })
|
|
310
|
+
ref.current?.scrollToBottom({ behavior: 'smooth' })
|
|
311
|
+
ref.current?.getFirstVisibleIndex() // Number
|
|
312
|
+
ref.current?.getLastVisibleIndex() // Number
|
|
313
|
+
ref.current?.getScrollElement() // HTMLElement
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
## Common Issues
|
|
318
|
+
|
|
319
|
+
| Issue | Fix |
|
|
320
|
+
|-------|-----|
|
|
321
|
+
| Items flashing | Increase `overscan`, check skeleton height matches real item |
|
|
322
|
+
| Scroll jumps | Ensure skeleton matches real item height exactly |
|
|
323
|
+
| Not scrolling to new items | Check `reverse` prop, use `scrollToIndex` with `behavior: 'smooth'` |
|
|
324
|
+
| Memory grows | Ensure `overscan` not too high, items unmount correctly |
|
|
325
|
+
| TypeScript errors | Import `VirtuosoHandle` from `react-virtuoso` |
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
## Related Skills
|
|
329
|
+
|
|
330
|
+
- `ui/ask-virtual-list` - Pattern guide for virtual lists
|
|
331
|
+
- `ui/ask-tanstack-table` - Virtualized tables
|
|
332
|
+
- `ui/ui-engineering` - Master orchestrator
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: email-craft
|
|
3
|
+
description: Email design system for product emails. Use when building, redesigning, or auditing HTML emails — daily digests, transactional, onboarding, announcements. Encodes the constraints of email clients (table layout, inline CSS, dark mode, MSO) and the product principles: single focal point, decision-oriented hierarchy, action-driven CTAs, restraint over decoration.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Principle: Email is not web
|
|
7
|
+
|
|
8
|
+
Email clients have no shared rendering engine. What works in Apple Mail breaks in Outlook. What works in Gmail web breaks in Gmail app. The only reliable substrate is **tables + inline CSS**.
|
|
9
|
+
|
|
10
|
+
Default constraints:
|
|
11
|
+
- 600px max width, single column
|
|
12
|
+
- Table-based/hybrid layout
|
|
13
|
+
- All CSS inlined (no `<style>` blocks for layout)
|
|
14
|
+
- 16px minimum body text where possible
|
|
15
|
+
- Strong vertical rhythm (8px baseline)
|
|
16
|
+
- One primary CTA per email
|
|
17
|
+
- One visual focal point
|
|
18
|
+
- Restrained borders (hairlines, not cards)
|
|
19
|
+
- No gratuitous cards/boxes
|
|
20
|
+
- Mobile-first hierarchy (stack on <600px)
|
|
21
|
+
- Dark-mode handling via `@media (prefers-color-scheme: dark)`
|
|
22
|
+
- Accessible link contrast (4.5:1 minimum)
|
|
23
|
+
- Alt text on all images
|
|
24
|
+
- Plaintext fallback required
|
|
25
|
+
|
|
26
|
+
## Principle: Decision surface, not database dump
|
|
27
|
+
|
|
28
|
+
The email answers four questions:
|
|
29
|
+
|
|
30
|
+
1. **What changed today?** (numbers, freshness)
|
|
31
|
+
2. **What should I look at?** (1 hero + 3–5 compact)
|
|
32
|
+
3. **Why should I care?** (reasoning, signals, gaps)
|
|
33
|
+
4. **What should I do next?** (action list)
|
|
34
|
+
|
|
35
|
+
Everything else links to the dashboard.
|
|
36
|
+
|
|
37
|
+
## Information architecture template
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
Header (brand + date + context)
|
|
41
|
+
↓
|
|
42
|
+
Today's numbers (4 stats max, hairline-separated)
|
|
43
|
+
↓
|
|
44
|
+
Hero job (tier 1 — full reasoning, primary CTA)
|
|
45
|
+
↓
|
|
46
|
+
3–5 compact jobs (tier 2 — score, title, company, link)
|
|
47
|
+
↓
|
|
48
|
+
Market signal / insight (recurring editorial feature)
|
|
49
|
+
↓
|
|
50
|
+
People to reach out to (if applicable)
|
|
51
|
+
↓
|
|
52
|
+
Your move (3 numbered actions)
|
|
53
|
+
↓
|
|
54
|
+
Footer (product identity + unsubscribe)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Tier definitions
|
|
58
|
+
|
|
59
|
+
| Tier | Count | Treatment |
|
|
60
|
+
|------|-------|-----------|
|
|
61
|
+
| 1 (Hero) | 1 | Full card: company → role → score → why → CTA |
|
|
62
|
+
| 2 (Worth time) | 3–5 | Compact row: score · title @ company · location · CTA |
|
|
63
|
+
| 3 (Everything else) | N | Not in email. "See all N jobs →" link to dashboard |
|
|
64
|
+
|
|
65
|
+
## Visual system
|
|
66
|
+
|
|
67
|
+
### Tokens (Apple-inspired neutral)
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const tokens = {
|
|
71
|
+
bg: "#f5f5f7",
|
|
72
|
+
card: "#ffffff",
|
|
73
|
+
ink: "#111111",
|
|
74
|
+
body: "#2b2b2e",
|
|
75
|
+
muted: "#6e6e73",
|
|
76
|
+
faint: "#86868b",
|
|
77
|
+
line: "#e8e8ed",
|
|
78
|
+
subLine: "#f0f0f3",
|
|
79
|
+
accent: "#111111", // primary actions
|
|
80
|
+
success: "#1a7f37", // strong match
|
|
81
|
+
warn: "#925400", // review
|
|
82
|
+
danger: "#b42318", // red flags
|
|
83
|
+
tagBg: "#f2f2f5",
|
|
84
|
+
tagBd: "#e8e8ec",
|
|
85
|
+
radius: "10px",
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Dark mode overrides (in `<style>` block)
|
|
90
|
+
|
|
91
|
+
Force specific values for Gmail/Outlook auto-invert:
|
|
92
|
+
- Score pills: force dark backgrounds
|
|
93
|
+
- Primary CTA: invert to light bg + dark text
|
|
94
|
+
- Hairlines: `#2c2c2e`
|
|
95
|
+
- Text colors: shift to `#f5f5f7` / `#d6d6d8` / `#a1a1a6`
|
|
96
|
+
|
|
97
|
+
### Typography
|
|
98
|
+
|
|
99
|
+
- Display: `-apple-system...`, 24px, weight 700, tracking -0.025em
|
|
100
|
+
- Section title: 13px, weight 600, tracking -0.01em, no uppercase
|
|
101
|
+
- Body: 12.5px, line-height 1.65, max-width 65ch
|
|
102
|
+
- Small caps (brand only): 10px, weight 700, tracking 0.14em, uppercase
|
|
103
|
+
- Score: 22px, weight 700, tracking -0.02em
|
|
104
|
+
- Micro: 10-11px, muted
|
|
105
|
+
|
|
106
|
+
### Spacing
|
|
107
|
+
|
|
108
|
+
- Section gap: 28px
|
|
109
|
+
- Internal card padding: 20px
|
|
110
|
+
- Row padding: 16px
|
|
111
|
+
- Hairline margin: 14px above/below
|
|
112
|
+
|
|
113
|
+
## Component library
|
|
114
|
+
|
|
115
|
+
### `emailHeader(vm)`
|
|
116
|
+
Brand + date + profile context (roles · locations)
|
|
117
|
+
|
|
118
|
+
### `emailStats(vm)`
|
|
119
|
+
4-column stat row: Scanned · Fresh · Strong · To Review
|
|
120
|
+
Hairline top/bottom, no card wrapper
|
|
121
|
+
|
|
122
|
+
### `emailHeroJob(match)`
|
|
123
|
+
**The focal point.** Full hierarchy:
|
|
124
|
+
- Section title: "Today's best match"
|
|
125
|
+
- Score badge + verdict pill
|
|
126
|
+
- Role (17px, weight 700)
|
|
127
|
+
- Company (13px, weight 500)
|
|
128
|
+
- Meta: location · posted · compensation
|
|
129
|
+
- Matched skills (tags, max 5)
|
|
130
|
+
- **Why this matches** (bulleted reasoning from `whyMatch`)
|
|
131
|
+
- **Watch** (red flags, if any)
|
|
132
|
+
- Primary CTA: `[ Apply to {company} → ]` (contextual verb)
|
|
133
|
+
- Secondary: `[ Review match → ]`
|
|
134
|
+
|
|
135
|
+
### `emailJobRow(matches[])`
|
|
136
|
+
Compact rows, hairline-separated:
|
|
137
|
+
- Score (13px, bold) + `/5` (9px, faint)
|
|
138
|
+
- Title (link, 13px, weight 600)
|
|
139
|
+
- Company (12px, muted)
|
|
140
|
+
- Location · posted · compensation (10.5px, faint)
|
|
141
|
+
- Tags (max 3)
|
|
142
|
+
- Single red flag (if any)
|
|
143
|
+
- CTA: `[ View → ]`
|
|
144
|
+
|
|
145
|
+
### `emailInsight(vm)`
|
|
146
|
+
Recurring "Market Signal" or "Why JobOps picked this" section.
|
|
147
|
+
- Section title
|
|
148
|
+
- Narrative sentence: "Rust appeared in 42% of today's strong matches"
|
|
149
|
+
- Your level vs market demand (two-column)
|
|
150
|
+
- CTA: `[ Explore skill gap → ]` or `[ See why → ]`
|
|
151
|
+
|
|
152
|
+
### `emailOutreach(vm)`
|
|
153
|
+
Companies with relevant roles:
|
|
154
|
+
- Company name
|
|
155
|
+
- Role count
|
|
156
|
+
- 3 LinkedIn search links
|
|
157
|
+
- No cards — just hairline-separated rows
|
|
158
|
+
|
|
159
|
+
### `emailActionList(vm.actions)`
|
|
160
|
+
Numbered "Your move" list:
|
|
161
|
+
```
|
|
162
|
+
01 Apply to Stripe
|
|
163
|
+
Strongest match today — 9.2/5
|
|
164
|
+
|
|
165
|
+
02 Review Vercel
|
|
166
|
+
Good role, compensation unclear
|
|
167
|
+
|
|
168
|
+
03 Learn Rust
|
|
169
|
+
Appeared in 8 of 19 target roles
|
|
170
|
+
```
|
|
171
|
+
Each links to dashboard action.
|
|
172
|
+
|
|
173
|
+
### `emailFooter(vm)`
|
|
174
|
+
Product identity:
|
|
175
|
+
```
|
|
176
|
+
JobOps
|
|
177
|
+
Your daily job intelligence.
|
|
178
|
+
|
|
179
|
+
12 jobs analyzed · 3 worth your attention
|
|
180
|
+
|
|
181
|
+
[ Open dashboard ] [ Settings ] [ Unsubscribe ]
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## CTA principles
|
|
185
|
+
|
|
186
|
+
| Weak | Strong |
|
|
187
|
+
|------|--------|
|
|
188
|
+
| `View posting →` | `Apply to Stripe →` |
|
|
189
|
+
| `Details` | `Review match →` |
|
|
190
|
+
| `View` | `Open job →` |
|
|
191
|
+
| `Learn more` | `Explore skill gap →` |
|
|
192
|
+
|
|
193
|
+
The verb tells the user what happens next.
|
|
194
|
+
|
|
195
|
+
## "Why JobOps picked this" pattern
|
|
196
|
+
|
|
197
|
+
Replace generic score with reasoning:
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
WHY JOBOPS PICKED THIS
|
|
201
|
+
|
|
202
|
+
✓ 94% role fit (React, TypeScript, distributed systems)
|
|
203
|
+
✓ Remote — matches your preference
|
|
204
|
+
✓ Matches 8 of 9 required skills
|
|
205
|
+
✗ Requires AWS certification (you: learning)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Or compact:
|
|
209
|
+
```
|
|
210
|
+
Strong match because your TypeScript/React experience
|
|
211
|
+
overlaps with 4 of 5 core requirements.
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Pre-implementation checklist
|
|
215
|
+
|
|
216
|
+
Before writing any email HTML:
|
|
217
|
+
|
|
218
|
+
1. Identify the **single job** of this email
|
|
219
|
+
2. Define the **information hierarchy** (what must be seen vs. what can be linked)
|
|
220
|
+
3. Select **one focal element** (hero job, key insight, primary action)
|
|
221
|
+
4. Define **primary action** (what should the user do?)
|
|
222
|
+
5. **Remove** anything that doesn't support that action
|
|
223
|
+
6. Design desktop (600px)
|
|
224
|
+
7. Design mobile (stack, full-width CTAs)
|
|
225
|
+
8. Test: Gmail (web/app), Outlook (Win/Mac), Apple Mail, Yahoo
|
|
226
|
+
|
|
227
|
+
## File structure for email systems
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
lib/email/
|
|
231
|
+
tokens.ts # colors, spacing, typography, radius
|
|
232
|
+
components.ts # header, stats, hero, row, insight, outreach, actions, footer
|
|
233
|
+
accessibility.ts # alt text, contrast, ARIA roles, plaintext
|
|
234
|
+
preview.ts # generate preview HTML for browser testing
|
|
235
|
+
renderer.ts # compose(viewModel) → { html, text }
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
The renderer becomes compositional, not one large generator.
|
|
239
|
+
|
|
240
|
+
## Anti-patterns to avoid
|
|
241
|
+
|
|
242
|
+
- ❌ Multiple cards stacked (dashboard export feel)
|
|
243
|
+
- ❌ Gradients, brand color explosions
|
|
244
|
+
- ❌ "View" / "Details" / "Learn more" as only CTAs
|
|
245
|
+
- ❌ Showing all N jobs in email
|
|
246
|
+
- ❌ Generic "Strong match" without reasoning
|
|
247
|
+
- ❌ Missing plaintext version
|
|
248
|
+
- ❌ No dark-mode handling
|
|
249
|
+
- ❌ Images without alt text
|
|
250
|
+
- ❌ Fixed px widths on inner tables (use 100%)
|
|
251
|
+
- ❌ `div` for layout (use `table role="presentation"`)
|
|
252
|
+
|
|
253
|
+
## References
|
|
254
|
+
|
|
255
|
+
- Medium newsletter guidance: scannable sections, prominent links, clear purpose
|
|
256
|
+
- CodeRabbit: summary → findings → actionable details → deep links
|
|
257
|
+
- Apple Mail / Outlook / Gmail rendering quirks
|
|
258
|
+
- WCAG 2.1 AA for email (contrast, focus, semantic structure)
|