wizz-method 1.3.2 → 1.4.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/.claude-plugin/marketplace.json +4 -4
- package/README.md +1 -1
- package/evals/routing/dataset.json +290 -0
- package/evals/routing/run-routing-eval.mjs +89 -0
- package/package.json +11 -3
- package/skills-registry.yaml +72 -25
- package/src/modules/wizz/agents/wizz-ads/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-copy/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-designer/SKILL.md +6 -8
- package/src/modules/wizz/agents/wizz-growth/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-maestro/SKILL.md +6 -5
- package/src/modules/wizz/agents/wizz-memoria/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-qa/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-seo/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-social/SKILL.md +1 -1
- package/src/skills-lib/ai-product-design/SKILL.md +85 -0
- package/src/skills-lib/ai-product-design/references/LICENSE +21 -0
- package/src/skills-lib/ai-product-design/references/ai-alignment-reasoning/bias-detection-design/SKILL.md +37 -0
- package/src/skills-lib/ai-product-design/references/ai-alignment-reasoning/consent-and-agency/SKILL.md +37 -0
- package/src/skills-lib/ai-product-design/references/ai-alignment-reasoning/escalation-design/SKILL.md +40 -0
- package/src/skills-lib/ai-product-design/references/ai-alignment-reasoning/guardrail-design/SKILL.md +36 -0
- package/src/skills-lib/ai-product-design/references/ai-alignment-reasoning/harm-anticipation/SKILL.md +108 -0
- package/src/skills-lib/ai-product-design/references/ai-alignment-reasoning/transparency-patterns/SKILL.md +36 -0
- package/src/skills-lib/ai-product-design/references/ai-alignment-reasoning/trust-calibration/SKILL.md +102 -0
- package/src/skills-lib/ai-product-design/references/ai-alignment-reasoning/value-specification/SKILL.md +39 -0
- package/src/skills-lib/ai-product-design/references/design-agent-orchestration/agent-role-design/SKILL.md +37 -0
- package/src/skills-lib/ai-product-design/references/design-agent-orchestration/failure-recovery/SKILL.md +37 -0
- package/src/skills-lib/ai-product-design/references/design-agent-orchestration/handoff-protocols/SKILL.md +45 -0
- package/src/skills-lib/ai-product-design/references/design-agent-orchestration/human-in-the-loop/SKILL.md +39 -0
- package/src/skills-lib/ai-product-design/references/design-agent-orchestration/observability-design/SKILL.md +48 -0
- package/src/skills-lib/ai-product-design/references/design-agent-orchestration/state-management/SKILL.md +95 -0
- package/src/skills-lib/ai-product-design/references/design-agent-orchestration/task-decomposition/SKILL.md +36 -0
- package/src/skills-lib/ai-product-design/references/evaluation/comparative-evaluation/SKILL.md +39 -0
- package/src/skills-lib/ai-product-design/references/evaluation/failure-taxonomy/SKILL.md +47 -0
- package/src/skills-lib/ai-product-design/references/evaluation/heuristic-evaluation-ai/SKILL.md +46 -0
- package/src/skills-lib/ai-product-design/references/evaluation/longitudinal-measurement/SKILL.md +38 -0
- package/src/skills-lib/ai-product-design/references/evaluation/output-quality-rubrics/SKILL.md +41 -0
- package/src/skills-lib/ai-product-design/references/evaluation/task-success-metrics/SKILL.md +35 -0
- package/src/skills-lib/ai-product-design/references/evaluation/user-satisfaction-signals/SKILL.md +50 -0
- package/src/skills-lib/ai-product-design/references/model-interaction-design/context-window-design/SKILL.md +28 -0
- package/src/skills-lib/ai-product-design/references/model-interaction-design/conversation-patterns/SKILL.md +37 -0
- package/src/skills-lib/ai-product-design/references/model-interaction-design/feedback-loops/SKILL.md +35 -0
- package/src/skills-lib/ai-product-design/references/model-interaction-design/frustration-detection/SKILL.md +91 -0
- package/src/skills-lib/ai-product-design/references/model-interaction-design/generative-ui/SKILL.md +34 -0
- package/src/skills-lib/ai-product-design/references/model-interaction-design/mixed-initiative-flow/SKILL.md +39 -0
- package/src/skills-lib/ai-product-design/references/model-interaction-design/multimodal-orchestration/SKILL.md +94 -0
- package/src/skills-lib/ai-product-design/references/model-interaction-design/progressive-disclosure/SKILL.md +33 -0
- package/src/skills-lib/ai-product-design/references/prompt-architecture/chain-of-thought-design/SKILL.md +45 -0
- package/src/skills-lib/ai-product-design/references/prompt-architecture/constraint-specification/SKILL.md +50 -0
- package/src/skills-lib/ai-product-design/references/prompt-architecture/context-engineering/SKILL.md +45 -0
- package/src/skills-lib/ai-product-design/references/prompt-architecture/few-shot-patterns/SKILL.md +47 -0
- package/src/skills-lib/ai-product-design/references/prompt-architecture/prompt-versioning/SKILL.md +42 -0
- package/src/skills-lib/ai-product-design/references/prompt-architecture/system-prompt-structure/SKILL.md +50 -0
- package/src/skills-lib/ai-product-design/references/prompt-architecture/template-design/SKILL.md +42 -0
- package/src/skills-lib/ai-product-design/references/system-behavior-shaping/behavioral-consistency/SKILL.md +36 -0
- package/src/skills-lib/ai-product-design/references/system-behavior-shaping/cultural-adaptation/SKILL.md +38 -0
- package/src/skills-lib/ai-product-design/references/system-behavior-shaping/domain-voice/SKILL.md +51 -0
- package/src/skills-lib/ai-product-design/references/system-behavior-shaping/emotional-design/SKILL.md +51 -0
- package/src/skills-lib/ai-product-design/references/system-behavior-shaping/error-personality/SKILL.md +45 -0
- package/src/skills-lib/ai-product-design/references/system-behavior-shaping/persona-architecture/SKILL.md +38 -0
- package/src/skills-lib/ai-product-design/references/system-behavior-shaping/tone-calibration/SKILL.md +39 -0
- package/src/skills-lib/algorithmic-art/LICENSE.txt +202 -0
- package/src/skills-lib/algorithmic-art/SKILL.md +405 -0
- package/src/skills-lib/algorithmic-art/templates/generator_template.js +223 -0
- package/src/skills-lib/algorithmic-art/templates/viewer.html +599 -0
- package/src/skills-lib/animate/README.md +41 -0
- package/src/skills-lib/animate/SKILL.md +179 -0
- package/src/skills-lib/animate/examples/animate-height.tsx +75 -0
- package/src/skills-lib/animate/examples/app-store-card.tsx +164 -0
- package/src/skills-lib/animate/examples/card-hover.tsx +79 -0
- package/src/skills-lib/animate/examples/feedback-popover.tsx +119 -0
- package/src/skills-lib/animate/examples/multi-step-flow.tsx +128 -0
- package/src/skills-lib/animate/examples/shared-layout.tsx +64 -0
- package/src/skills-lib/animate/examples/text-reveal.tsx +70 -0
- package/src/skills-lib/animate/examples/toast-stacking.tsx +95 -0
- package/src/skills-lib/animate/references/css-animations.md +232 -0
- package/src/skills-lib/animate/references/easing-and-timing.md +105 -0
- package/src/skills-lib/animate/references/framer-motion.md +356 -0
- package/src/skills-lib/animate/references/performance-accessibility.md +185 -0
- package/src/skills-lib/auth-and-secrets/SKILL.md +38 -10
- package/src/skills-lib/canvas-design/LICENSE.txt +202 -0
- package/src/skills-lib/canvas-design/SKILL.md +130 -0
- package/src/skills-lib/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -0
- package/src/skills-lib/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Italiana-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Jura-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/PoiretOne-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/src/skills-lib/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
- package/src/skills-lib/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/src/skills-lib/design-motion-principles/SKILL.md +122 -0
- package/src/skills-lib/design-motion-principles/references/accessibility.md +52 -0
- package/src/skills-lib/design-motion-principles/references/anti-checklist.md +274 -0
- package/src/skills-lib/design-motion-principles/references/audit-checklist.md +137 -0
- package/src/skills-lib/design-motion-principles/references/creation-gotchas.md +36 -0
- package/src/skills-lib/design-motion-principles/references/demo-shell.html +275 -0
- package/src/skills-lib/design-motion-principles/references/emil-kowalski.md +121 -0
- package/src/skills-lib/design-motion-principles/references/jakub-krehel.md +80 -0
- package/src/skills-lib/design-motion-principles/references/jhey-tompkins.md +122 -0
- package/src/skills-lib/design-motion-principles/references/motion-cookbook.md +529 -0
- package/src/skills-lib/design-motion-principles/references/output-format.md +346 -0
- package/src/skills-lib/design-motion-principles/references/performance.md +82 -0
- package/src/skills-lib/design-motion-principles/references/report-template.html +1006 -0
- package/src/skills-lib/design-motion-principles/workflows/audit.md +203 -0
- package/src/skills-lib/design-motion-principles/workflows/create.md +87 -0
- package/src/skills-lib/desktop-security/SKILL.md +35 -4
- package/src/skills-lib/premium-landing-ui-researcher/SKILL.md +52 -2083
- package/src/skills-lib/premium-landing-ui-researcher/references/audit-protocol.md +124 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/component-sources.md +336 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/core-goal.md +62 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/dashboard-and-portfolio-modes.md +92 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/handoffs.md +55 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/landing-page-strategy.md +227 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/mandatory-process.md +36 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/output-format-and-quality.md +129 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/prompt-templates.md +206 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/site-levels.md +515 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/source-first-protocol.md +144 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/source-links.md +39 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/stack-and-visual-direction.md +118 -0
- package/src/skills-lib/taste-skill/SKILL.md +33 -1177
- package/src/skills-lib/taste-skill/references/anti-slop-tells.md +110 -0
- package/src/skills-lib/taste-skill/references/architecture-conventions.md +39 -0
- package/src/skills-lib/taste-skill/references/block-library.md +61 -0
- package/src/skills-lib/taste-skill/references/brief-and-dials.md +89 -0
- package/src/skills-lib/taste-skill/references/dark-mode.md +23 -0
- package/src/skills-lib/taste-skill/references/design-directives.md +191 -0
- package/src/skills-lib/taste-skill/references/design-systems.md +267 -0
- package/src/skills-lib/taste-skill/references/motion-patterns.md +167 -0
- package/src/skills-lib/taste-skill/references/pattern-vocabulary.md +78 -0
- package/src/skills-lib/taste-skill/references/performance-a11y.md +33 -0
- package/src/skills-lib/taste-skill/references/preflight-checklist.md +73 -0
- package/src/skills-lib/taste-skill/references/redesign-protocol.md +52 -0
- package/src/skills-lib/web-security/SKILL.md +74 -6
- package/src/skills-lib/wizz-router/SKILL.md +21 -243
- package/src/skills-lib/wizz-router/references/auditoria-360.md +21 -0
- package/src/skills-lib/wizz-router/references/routing-table-flat.md +95 -0
- package/src/squads/advisory-board/agents/ray-dalio.md +0 -20
- package/tools/hooks/README.md +17 -0
- package/tools/hooks/security-defensive-context.js +70 -0
- package/tools/hooks/session-rules.js +38 -0
- package/tools/hooks/wizz-router-enforce.js +188 -0
- package/tools/installer/modules/skills-lib.js +70 -1
- package/tools/sync-global.mjs +84 -0
- package/.claude/settings.local.json +0 -8
- package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-314.pyc +0 -0
- package/src/skills-lib/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/src/skills-lib/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/src/skills-lib/ui-ux-pro-max/scripts/__pycache__/search.cpython-314.pyc +0 -0
- package/src/skills-lib/ui-ux-pro-max/scripts/graphify-out/cache/0964cef300f753b5cee0cc49777ca97035c85d45c212099405d9ee4da36e523f.json +0 -1
- package/src/skills-lib/ui-ux-pro-max/scripts/graphify-out/cache/21a40f6810c3a6f17acc3580f106171daf78c7b41bbd066bc80448a821ea0adf.json +0 -1
- package/src/skills-lib/ui-ux-pro-max/scripts/graphify-out/cache/69ea15a020ac81b8459e37ef63654398a50c1035fc01c43d8069df217389f7f3.json +0 -1
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# Framer Motion Reference
|
|
2
|
+
|
|
3
|
+
Framer Motion is the recommended animation library for React/Next.js applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install framer-motion
|
|
9
|
+
# or
|
|
10
|
+
pnpm add framer-motion
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Basic Usage
|
|
14
|
+
|
|
15
|
+
### Motion Components
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { motion } from "framer-motion"
|
|
19
|
+
|
|
20
|
+
// Any HTML element can become animated
|
|
21
|
+
<motion.div
|
|
22
|
+
initial={{ opacity: 0, y: 20 }}
|
|
23
|
+
animate={{ opacity: 1, y: 0 }}
|
|
24
|
+
transition={{ duration: 0.3, ease: "easeOut" }}
|
|
25
|
+
>
|
|
26
|
+
Content
|
|
27
|
+
</motion.div>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Common Props
|
|
31
|
+
|
|
32
|
+
| Prop | Description |
|
|
33
|
+
|------|-------------|
|
|
34
|
+
| `initial` | Starting state (or `false` to disable) |
|
|
35
|
+
| `animate` | Target state |
|
|
36
|
+
| `exit` | Exit state (requires AnimatePresence) |
|
|
37
|
+
| `transition` | Animation configuration |
|
|
38
|
+
| `whileHover` | State while hovered |
|
|
39
|
+
| `whileTap` | State while pressed |
|
|
40
|
+
| `whileFocus` | State while focused |
|
|
41
|
+
| `whileInView` | State while in viewport |
|
|
42
|
+
|
|
43
|
+
## AnimatePresence
|
|
44
|
+
|
|
45
|
+
Required for exit animations. Wraps elements that may be removed from DOM.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { motion, AnimatePresence } from "framer-motion"
|
|
49
|
+
|
|
50
|
+
function Modal({ isOpen, onClose, children }) {
|
|
51
|
+
return (
|
|
52
|
+
<AnimatePresence>
|
|
53
|
+
{isOpen && (
|
|
54
|
+
<motion.div
|
|
55
|
+
initial={{ opacity: 0 }}
|
|
56
|
+
animate={{ opacity: 1 }}
|
|
57
|
+
exit={{ opacity: 0 }}
|
|
58
|
+
transition={{ duration: 0.2 }}
|
|
59
|
+
>
|
|
60
|
+
{children}
|
|
61
|
+
</motion.div>
|
|
62
|
+
)}
|
|
63
|
+
</AnimatePresence>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### AnimatePresence Props
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<AnimatePresence
|
|
72
|
+
mode="wait" // 'sync' | 'wait' | 'popLayout'
|
|
73
|
+
initial={false} // Disable initial animation on mount
|
|
74
|
+
onExitComplete={() => {}} // Callback when all exits complete
|
|
75
|
+
>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- `mode="sync"` (default): Enter and exit happen simultaneously
|
|
79
|
+
- `mode="wait"`: Wait for exit to complete before enter
|
|
80
|
+
- `mode="popLayout"`: Remove exiting elements from layout flow
|
|
81
|
+
|
|
82
|
+
## Variants
|
|
83
|
+
|
|
84
|
+
Variants define animation states that can be orchestrated across parent/children.
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
const containerVariants = {
|
|
88
|
+
hidden: { opacity: 0 },
|
|
89
|
+
visible: {
|
|
90
|
+
opacity: 1,
|
|
91
|
+
transition: {
|
|
92
|
+
staggerChildren: 0.1, // Delay between each child
|
|
93
|
+
delayChildren: 0.2, // Initial delay before children start
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const itemVariants = {
|
|
99
|
+
hidden: { opacity: 0, y: 20 },
|
|
100
|
+
visible: { opacity: 1, y: 0 }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function List({ items }) {
|
|
104
|
+
return (
|
|
105
|
+
<motion.ul
|
|
106
|
+
variants={containerVariants}
|
|
107
|
+
initial="hidden"
|
|
108
|
+
animate="visible"
|
|
109
|
+
>
|
|
110
|
+
{items.map(item => (
|
|
111
|
+
<motion.li key={item.id} variants={itemVariants}>
|
|
112
|
+
{item.name}
|
|
113
|
+
</motion.li>
|
|
114
|
+
))}
|
|
115
|
+
</motion.ul>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Layout Animations
|
|
121
|
+
|
|
122
|
+
Animate layout changes automatically with the `layout` prop.
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
// Element animates when its position/size changes
|
|
126
|
+
<motion.div layout>
|
|
127
|
+
{isExpanded ? "Expanded content here" : "Collapsed"}
|
|
128
|
+
</motion.div>
|
|
129
|
+
|
|
130
|
+
// Layout types
|
|
131
|
+
<motion.div layout> // Animate position and size
|
|
132
|
+
<motion.div layout="position"> // Only animate position
|
|
133
|
+
<motion.div layout="size"> // Only animate size
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Shared Layout Animations (layoutId)
|
|
137
|
+
|
|
138
|
+
Elements with the same `layoutId` animate between each other.
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
function Tabs({ tabs, activeTab, setActiveTab }) {
|
|
142
|
+
return (
|
|
143
|
+
<div className="flex gap-2">
|
|
144
|
+
{tabs.map(tab => (
|
|
145
|
+
<button
|
|
146
|
+
key={tab}
|
|
147
|
+
onClick={() => setActiveTab(tab)}
|
|
148
|
+
className="relative px-4 py-2"
|
|
149
|
+
>
|
|
150
|
+
{tab}
|
|
151
|
+
{activeTab === tab && (
|
|
152
|
+
<motion.div
|
|
153
|
+
layoutId="tab-indicator"
|
|
154
|
+
className="absolute inset-0 bg-blue-500 rounded -z-10"
|
|
155
|
+
transition={{ type: "spring", stiffness: 400, damping: 30 }}
|
|
156
|
+
/>
|
|
157
|
+
)}
|
|
158
|
+
</button>
|
|
159
|
+
))}
|
|
160
|
+
</div>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### LayoutGroup
|
|
166
|
+
|
|
167
|
+
Group layout animations to prevent conflicts between independent animations.
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import { LayoutGroup } from "framer-motion"
|
|
171
|
+
|
|
172
|
+
<LayoutGroup>
|
|
173
|
+
<Tabs /> {/* These layout animations */}
|
|
174
|
+
</LayoutGroup>
|
|
175
|
+
<LayoutGroup>
|
|
176
|
+
<OtherTabs /> {/* Won't conflict with these */}
|
|
177
|
+
</LayoutGroup>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Gestures
|
|
181
|
+
|
|
182
|
+
### Drag
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
<motion.div
|
|
186
|
+
drag // Enable both axes
|
|
187
|
+
drag="x" // Horizontal only
|
|
188
|
+
drag="y" // Vertical only
|
|
189
|
+
dragConstraints={{ left: 0, right: 300, top: 0, bottom: 200 }}
|
|
190
|
+
dragElastic={0.2} // Elasticity outside constraints (0-1)
|
|
191
|
+
dragMomentum={true} // Continue after release
|
|
192
|
+
dragTransition={{ bounceStiffness: 600, bounceDamping: 20 }}
|
|
193
|
+
onDragStart={(event, info) => {}}
|
|
194
|
+
onDrag={(event, info) => {}}
|
|
195
|
+
onDragEnd={(event, info) => {}}
|
|
196
|
+
>
|
|
197
|
+
Drag me
|
|
198
|
+
</motion.div>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Drag Info Object
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
onDrag={(event, info) => {
|
|
205
|
+
info.point // { x, y } current position
|
|
206
|
+
info.delta // { x, y } change since last frame
|
|
207
|
+
info.offset // { x, y } offset from drag start
|
|
208
|
+
info.velocity // { x, y } current velocity
|
|
209
|
+
}}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Motion Values & Hooks
|
|
213
|
+
|
|
214
|
+
### useMotionValue
|
|
215
|
+
|
|
216
|
+
Create a value that updates without re-rendering.
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
import { motion, useMotionValue, useTransform } from "framer-motion"
|
|
220
|
+
|
|
221
|
+
function Component() {
|
|
222
|
+
const x = useMotionValue(0)
|
|
223
|
+
|
|
224
|
+
// Transform x position to opacity
|
|
225
|
+
const opacity = useTransform(x, [-100, 0, 100], [0, 1, 0])
|
|
226
|
+
|
|
227
|
+
// Transform x to rotation
|
|
228
|
+
const rotate = useTransform(x, [-100, 100], [-10, 10])
|
|
229
|
+
|
|
230
|
+
return (
|
|
231
|
+
<motion.div
|
|
232
|
+
drag="x"
|
|
233
|
+
style={{ x, opacity, rotate }}
|
|
234
|
+
/>
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### useSpring
|
|
240
|
+
|
|
241
|
+
Create a spring-animated motion value.
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
import { useSpring, useMotionValue } from "framer-motion"
|
|
245
|
+
|
|
246
|
+
function Component() {
|
|
247
|
+
const x = useMotionValue(0)
|
|
248
|
+
const springX = useSpring(x, { stiffness: 300, damping: 30 })
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<motion.div
|
|
252
|
+
onMouseMove={(e) => x.set(e.clientX)}
|
|
253
|
+
style={{ x: springX }}
|
|
254
|
+
/>
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### useScroll
|
|
260
|
+
|
|
261
|
+
Track scroll progress.
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
import { useScroll, useTransform, motion } from "framer-motion"
|
|
265
|
+
|
|
266
|
+
function ProgressBar() {
|
|
267
|
+
const { scrollYProgress } = useScroll()
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
<motion.div
|
|
271
|
+
className="fixed top-0 left-0 right-0 h-1 bg-blue-500 origin-left"
|
|
272
|
+
style={{ scaleX: scrollYProgress }}
|
|
273
|
+
/>
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Scroll within a container
|
|
278
|
+
const { scrollYProgress } = useScroll({
|
|
279
|
+
target: containerRef,
|
|
280
|
+
offset: ["start end", "end start"] // When to start/end tracking
|
|
281
|
+
})
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### useInView
|
|
285
|
+
|
|
286
|
+
Detect when element enters viewport.
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
import { useInView } from "framer-motion"
|
|
290
|
+
|
|
291
|
+
function Component() {
|
|
292
|
+
const ref = useRef(null)
|
|
293
|
+
const isInView = useInView(ref, {
|
|
294
|
+
once: true, // Only trigger once
|
|
295
|
+
margin: "-100px" // Trigger 100px before entering
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<motion.div
|
|
300
|
+
ref={ref}
|
|
301
|
+
initial={{ opacity: 0, y: 50 }}
|
|
302
|
+
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
|
303
|
+
/>
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Transition Options
|
|
309
|
+
|
|
310
|
+
```tsx
|
|
311
|
+
const transition = {
|
|
312
|
+
// Timing
|
|
313
|
+
duration: 0.3,
|
|
314
|
+
delay: 0.1,
|
|
315
|
+
|
|
316
|
+
// Easing
|
|
317
|
+
ease: "easeOut", // or array: [0.23, 1, 0.32, 1]
|
|
318
|
+
|
|
319
|
+
// Spring physics
|
|
320
|
+
type: "spring",
|
|
321
|
+
stiffness: 400,
|
|
322
|
+
damping: 30,
|
|
323
|
+
mass: 1,
|
|
324
|
+
|
|
325
|
+
// Or duration-based spring
|
|
326
|
+
type: "spring",
|
|
327
|
+
duration: 0.3,
|
|
328
|
+
bounce: 0.2,
|
|
329
|
+
|
|
330
|
+
// Per-property transitions
|
|
331
|
+
opacity: { duration: 0.2 },
|
|
332
|
+
x: { type: "spring", stiffness: 300 },
|
|
333
|
+
|
|
334
|
+
// Repeat
|
|
335
|
+
repeat: Infinity,
|
|
336
|
+
repeatType: "reverse", // "loop" | "reverse" | "mirror"
|
|
337
|
+
repeatDelay: 0.5,
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Multi-Step Animations (Keyframes)
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
<motion.div
|
|
345
|
+
animate={{
|
|
346
|
+
x: [0, 100, 0], // Move right then back
|
|
347
|
+
opacity: [0, 1, 1, 0], // Fade in, stay, fade out
|
|
348
|
+
scale: [1, 1.2, 1], // Grow then shrink
|
|
349
|
+
}}
|
|
350
|
+
transition={{
|
|
351
|
+
duration: 2,
|
|
352
|
+
times: [0, 0.5, 1], // When each keyframe occurs (0-1)
|
|
353
|
+
ease: "easeInOut",
|
|
354
|
+
}}
|
|
355
|
+
/>
|
|
356
|
+
```
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Performance & Accessibility Reference
|
|
2
|
+
|
|
3
|
+
## Performance
|
|
4
|
+
|
|
5
|
+
### Target: 60 FPS
|
|
6
|
+
|
|
7
|
+
Animations should run at 60 frames per second (16.67ms per frame) for smooth motion.
|
|
8
|
+
|
|
9
|
+
### GPU-Accelerated Properties
|
|
10
|
+
|
|
11
|
+
Only these properties are hardware-accelerated and won't cause layout recalculation:
|
|
12
|
+
|
|
13
|
+
| Property | Notes |
|
|
14
|
+
|----------|-------|
|
|
15
|
+
| `transform` | translate, scale, rotate |
|
|
16
|
+
| `opacity` | Fade effects |
|
|
17
|
+
|
|
18
|
+
### Properties to Avoid Animating
|
|
19
|
+
|
|
20
|
+
These trigger layout recalculation and are expensive:
|
|
21
|
+
|
|
22
|
+
- `width`, `height`
|
|
23
|
+
- `top`, `left`, `right`, `bottom`
|
|
24
|
+
- `margin`, `padding`
|
|
25
|
+
- `border-width`
|
|
26
|
+
- `font-size`
|
|
27
|
+
|
|
28
|
+
**Instead**: Use `transform: scale()` instead of width/height, `transform: translate()` instead of top/left.
|
|
29
|
+
|
|
30
|
+
### will-change
|
|
31
|
+
|
|
32
|
+
Hints to the browser that an element will animate:
|
|
33
|
+
|
|
34
|
+
```css
|
|
35
|
+
.will-animate {
|
|
36
|
+
will-change: transform, opacity;
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Caution**: Use sparingly. Apply only to elements that actually animate, and remove after animation completes if possible. Overuse can hurt performance.
|
|
41
|
+
|
|
42
|
+
### Hardware Acceleration Hack
|
|
43
|
+
|
|
44
|
+
Force GPU layer creation (use sparingly):
|
|
45
|
+
|
|
46
|
+
```css
|
|
47
|
+
.force-gpu {
|
|
48
|
+
transform: translateZ(0);
|
|
49
|
+
/* or */
|
|
50
|
+
backface-visibility: hidden;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Monitoring Performance
|
|
55
|
+
|
|
56
|
+
1. **Chrome DevTools**: Performance tab → Record during animation
|
|
57
|
+
2. **Rendering tab**: Enable "FPS meter" and "Paint flashing"
|
|
58
|
+
3. **Layers panel**: Check layer composition
|
|
59
|
+
|
|
60
|
+
### Common Performance Issues
|
|
61
|
+
|
|
62
|
+
| Issue | Solution |
|
|
63
|
+
|-------|----------|
|
|
64
|
+
| Janky scrolling | Use `transform` instead of `top/left` |
|
|
65
|
+
| Slow animations | Reduce number of animated elements |
|
|
66
|
+
| Memory spikes | Avoid creating new objects during animation |
|
|
67
|
+
| Layout thrashing | Batch DOM reads/writes |
|
|
68
|
+
|
|
69
|
+
## Accessibility
|
|
70
|
+
|
|
71
|
+
### prefers-reduced-motion
|
|
72
|
+
|
|
73
|
+
Users can indicate they prefer reduced motion in their system settings. Always respect this preference.
|
|
74
|
+
|
|
75
|
+
#### CSS Implementation
|
|
76
|
+
|
|
77
|
+
```css
|
|
78
|
+
/* Full animation by default */
|
|
79
|
+
.element {
|
|
80
|
+
transition: transform 300ms ease-out;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Reduced or no motion for users who prefer it */
|
|
84
|
+
@media (prefers-reduced-motion: reduce) {
|
|
85
|
+
.element {
|
|
86
|
+
transition: none;
|
|
87
|
+
/* or minimal transition */
|
|
88
|
+
transition: opacity 200ms ease-out;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### React/Framer Motion Implementation
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { useReducedMotion } from "framer-motion"
|
|
97
|
+
|
|
98
|
+
function AnimatedComponent() {
|
|
99
|
+
const shouldReduceMotion = useReducedMotion()
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<motion.div
|
|
103
|
+
initial={{ opacity: 0, y: shouldReduceMotion ? 0 : 20 }}
|
|
104
|
+
animate={{ opacity: 1, y: 0 }}
|
|
105
|
+
transition={{ duration: shouldReduceMotion ? 0 : 0.3 }}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Global Configuration with MotionConfig
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import { MotionConfig, useReducedMotion } from "framer-motion"
|
|
115
|
+
|
|
116
|
+
function App() {
|
|
117
|
+
const shouldReduceMotion = useReducedMotion()
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<MotionConfig reducedMotion={shouldReduceMotion ? "always" : "never"}>
|
|
121
|
+
{/* All motion components inside respect this setting */}
|
|
122
|
+
<YourApp />
|
|
123
|
+
</MotionConfig>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### MotionConfig Options
|
|
129
|
+
|
|
130
|
+
| Value | Effect |
|
|
131
|
+
|-------|--------|
|
|
132
|
+
| `"never"` | Normal animations (default) |
|
|
133
|
+
| `"always"` | Skip all animations |
|
|
134
|
+
| `"user"` | Respect system `prefers-reduced-motion` setting |
|
|
135
|
+
|
|
136
|
+
### Accessible Animation Guidelines
|
|
137
|
+
|
|
138
|
+
1. **Provide alternatives**: Ensure content is accessible without animation
|
|
139
|
+
2. **Avoid flashing**: No content flashing more than 3 times per second
|
|
140
|
+
3. **Don't block interaction**: Animations shouldn't prevent users from interacting
|
|
141
|
+
4. **Keep it brief**: Long animations can be frustrating
|
|
142
|
+
5. **Purposeful motion**: Only animate when it adds value
|
|
143
|
+
6. **Pause controls**: For continuous animations, provide pause/stop
|
|
144
|
+
|
|
145
|
+
### Focus Management with Animations
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
// Ensure focus moves appropriately with animated elements
|
|
149
|
+
function Modal({ isOpen }) {
|
|
150
|
+
const modalRef = useRef(null)
|
|
151
|
+
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (isOpen) {
|
|
154
|
+
// Wait for enter animation before focusing
|
|
155
|
+
const timer = setTimeout(() => {
|
|
156
|
+
modalRef.current?.focus()
|
|
157
|
+
}, 300)
|
|
158
|
+
return () => clearTimeout(timer)
|
|
159
|
+
}
|
|
160
|
+
}, [isOpen])
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<AnimatePresence>
|
|
164
|
+
{isOpen && (
|
|
165
|
+
<motion.div
|
|
166
|
+
ref={modalRef}
|
|
167
|
+
tabIndex={-1}
|
|
168
|
+
initial={{ opacity: 0 }}
|
|
169
|
+
animate={{ opacity: 1 }}
|
|
170
|
+
exit={{ opacity: 0 }}
|
|
171
|
+
>
|
|
172
|
+
Modal content
|
|
173
|
+
</motion.div>
|
|
174
|
+
)}
|
|
175
|
+
</AnimatePresence>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Testing Reduced Motion
|
|
181
|
+
|
|
182
|
+
1. **macOS**: System Preferences → Accessibility → Display → Reduce motion
|
|
183
|
+
2. **Windows**: Settings → Ease of Access → Display → Show animations
|
|
184
|
+
3. **iOS**: Settings → Accessibility → Motion → Reduce Motion
|
|
185
|
+
4. **Chrome DevTools**: Rendering tab → Emulate CSS media feature `prefers-reduced-motion`
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: auth-and-secrets
|
|
3
3
|
description: >
|
|
4
4
|
Segurança de autenticação e gestão de secrets. Usar quando: implementar auth, lidar com JWT/OAuth/SSO,
|
|
5
|
+
token no header vs URL, revogação de token no logout, enumeração de usuário/mensagem genérica,
|
|
5
6
|
guardar API keys ou credenciais, configurar Clerk/Supabase auth, revisar onde secrets estão armazenados,
|
|
6
7
|
rotação de credenciais, checar se .env está seguro.
|
|
7
8
|
---
|
|
@@ -12,16 +13,25 @@ description: >
|
|
|
12
13
|
|
|
13
14
|
### Senhas
|
|
14
15
|
- Hash com bcrypt (custo >= 12), Argon2id ou scrypt. Nunca MD5/SHA1 para senhas
|
|
15
|
-
-
|
|
16
|
+
- Mínimo de 8 caracteres, sem restrição de caracteres especiais
|
|
16
17
|
- Implemente bloqueio após N tentativas (lockout ou CAPTCHA)
|
|
17
|
-
-
|
|
18
|
+
- Ofereça 2FA: TOTP (Google Authenticator) é o padrão mínimo
|
|
18
19
|
|
|
19
20
|
### JWT
|
|
20
|
-
-
|
|
21
|
+
- Na verificação, aceite só o algoritmo esperado: rejeite `alg: none` e não deixe o token escolher o algoritmo (ataque de confusão de algoritmo)
|
|
22
|
+
- Prefira RS256 (assimétrico) quando mais de um serviço verifica o token. HS256 só com secret forte: 32+ bytes aleatórios, nunca uma palavra
|
|
21
23
|
- exp curto para access token (15 min a 1h)
|
|
22
24
|
- Refresh token com rotação: ao usar, invalide o anterior e emita novo
|
|
23
25
|
- Nunca coloque dados sensíveis no payload (é base64, não criptografia)
|
|
24
|
-
-
|
|
26
|
+
- **Token no header `Authorization: Bearer`, NUNCA na URL/query string** (a URL vaza em logs de servidor, histórico do browser, header `Referer` e analytics)
|
|
27
|
+
- **Logout de verdade revoga o token**: coloque na blacklist (Redis, TTL = exp do token) e invalide o refresh token. Sem isso o access token continua válido depois do logout até expirar sozinho
|
|
28
|
+
|
|
29
|
+
### Enumeração de usuário
|
|
30
|
+
Evita que um atacante descubra quais e-mails existem na base (login, signup, reset de senha).
|
|
31
|
+
- Resposta idêntica para e-mail existente e inexistente: "Se o e-mail existir, enviamos um link"
|
|
32
|
+
- No login use mensagem genérica ("credenciais inválidas"), nunca "e-mail não cadastrado" vs "senha incorreta"
|
|
33
|
+
- Responda em **tempo constante**: rode o hash da senha mesmo quando o usuário não existe (senão o tempo de resposta denuncia quem existe)
|
|
34
|
+
- Reforce com rate limit por IP **e** por conta (ver web-security) para barrar varredura de e-mails
|
|
25
35
|
|
|
26
36
|
### OAuth / SSO
|
|
27
37
|
- Use biblioteca estabelecida, não implemente o flow manualmente
|
|
@@ -30,9 +40,10 @@ description: >
|
|
|
30
40
|
|
|
31
41
|
### Stack Supabase + Clerk
|
|
32
42
|
- Auth gerenciada pelo Clerk: nunca reimplementar flows de auth manualmente
|
|
33
|
-
-
|
|
43
|
+
- Toda rota protegida, Server Action e route handler chama `auth()` do Clerk no servidor. Nunca confiar em estado de sessão vindo do cliente
|
|
44
|
+
- Clerk webhook (`svix`) verificado por assinatura antes de processar: não confiar no body sem verificar
|
|
34
45
|
- Supabase RLS: toda tabela de domínio deve ter RLS ativo; queries devem filtrar por `workspace_id`/`user_id`
|
|
35
|
-
- `SUPABASE_SERVICE_ROLE_KEY` só
|
|
46
|
+
- `SUPABASE_SERVICE_ROLE_KEY` bypassa RLS: só em server-side (API routes, jobs), nunca exposta no frontend
|
|
36
47
|
- `NEXT_PUBLIC_*` = seguro expor no cliente; tudo sem `NEXT_PUBLIC_` = server-only
|
|
37
48
|
|
|
38
49
|
## Secrets e credenciais
|
|
@@ -46,19 +57,36 @@ description: >
|
|
|
46
57
|
### Onde guardar secrets
|
|
47
58
|
- Produção: variáveis de ambiente da plataforma (Vercel: `vercel env add`)
|
|
48
59
|
- CI/CD: variáveis de ambiente criptografadas da plataforma (GitHub Actions Secrets, etc.)
|
|
49
|
-
- Dev local: arquivo `.env.local` nunca commitado
|
|
60
|
+
- Dev local: arquivo `.env.local` nunca commitado. Verificar com `git check-ignore -v .env.local`
|
|
61
|
+
|
|
62
|
+
### API keys emitidas pelo próprio app
|
|
63
|
+
Se o app gera API keys para os usuários:
|
|
64
|
+
- Guarde só o hash (SHA-256) da key no banco, nunca a key em texto plano
|
|
65
|
+
- Mostre a key completa uma única vez, na criação
|
|
66
|
+
- Use prefixo identificável (ex: `wz_live_`) para facilitar detecção em scan de repositório
|
|
67
|
+
- Permita revogar e listar keys por usuário
|
|
50
68
|
|
|
51
69
|
### Cripto de credenciais de usuário (AES-256-GCM)
|
|
52
70
|
Se o app armazena credenciais de terceiros do usuário (ex: SMTP, WhatsApp API):
|
|
53
71
|
```ts
|
|
54
|
-
// src/lib/crypto.ts
|
|
55
|
-
|
|
72
|
+
// src/lib/crypto.ts: chave vem do env, nunca hardcodada
|
|
73
|
+
import { createCipheriv, randomBytes } from "crypto"
|
|
74
|
+
|
|
75
|
+
const key = process.env.CREDENTIAL_ENCRYPTION_KEY // base64, 32 bytes
|
|
56
76
|
if (!key) throw new Error("CREDENTIAL_ENCRYPTION_KEY não configurada")
|
|
57
|
-
|
|
77
|
+
|
|
78
|
+
export function encrypt(plain: string) {
|
|
79
|
+
const iv = randomBytes(12) // IV aleatório por registro, nunca reutilizar
|
|
80
|
+
const cipher = createCipheriv("aes-256-gcm", Buffer.from(key, "base64"), iv)
|
|
81
|
+
const enc = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()])
|
|
82
|
+
return { iv: iv.toString("base64"), data: enc.toString("base64"), tag: cipher.getAuthTag().toString("base64") }
|
|
83
|
+
}
|
|
84
|
+
// no decrypt: setAuthTag antes de final(), senão a integridade não é verificada
|
|
58
85
|
```
|
|
59
86
|
|
|
60
87
|
### Rotação de credenciais
|
|
61
88
|
- Rotacione secrets de terceiros a cada 90 dias ou após qualquer saída de membro do time
|
|
89
|
+
- Secret commitado por acidente: considere comprometido e rotacione na hora. Reescrever o histórico do git não basta
|
|
62
90
|
- API keys de produção: uma por serviço/ambiente, nunca compartilhadas
|
|
63
91
|
- Database passwords: use IAM auth quando disponível no cloud provider
|
|
64
92
|
|