@olives/devos 2.1.0
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/.agents/AGENTS.md +316 -0
- package/.agents/README.md +55 -0
- package/.agents/agents/architect.md +18 -0
- package/.agents/agents/dba.md +25 -0
- package/.agents/agents/developer.md +40 -0
- package/.agents/agents/devops.md +18 -0
- package/.agents/agents/memory-manager.md +38 -0
- package/.agents/agents/orchestrator.md +92 -0
- package/.agents/agents/qa.md +53 -0
- package/.agents/agents/release-manager.md +41 -0
- package/.agents/agents/researcher.md +19 -0
- package/.agents/agents/security.md +27 -0
- package/.agents/agents/tester.md +21 -0
- package/.agents/commands/README.md +42 -0
- package/.agents/commands/architect.md +16 -0
- package/.agents/commands/commit.md +15 -0
- package/.agents/commands/deploy.md +18 -0
- package/.agents/commands/fix.md +19 -0
- package/.agents/commands/refactor.md +19 -0
- package/.agents/commands/research.md +19 -0
- package/.agents/commands/review.md +21 -0
- package/.agents/commands/secure.md +23 -0
- package/.agents/commands/status.md +16 -0
- package/.agents/commands/test.md +16 -0
- package/.agents/scripts/commit.sh +73 -0
- package/.agents/scripts/install-hooks.sh +72 -0
- package/.agents/skills/agent-browser/SKILL.md +50 -0
- package/.agents/skills/agent-development/SKILL.md +415 -0
- package/.agents/skills/agent-development/examples/agent-creation-prompt.md +238 -0
- package/.agents/skills/agent-development/examples/complete-agent-examples.md +427 -0
- package/.agents/skills/agent-development/references/agent-creation-system-prompt.md +207 -0
- package/.agents/skills/agent-development/references/system-prompt-design.md +411 -0
- package/.agents/skills/agent-development/references/triggering-examples.md +491 -0
- package/.agents/skills/agent-development/scripts/validate-agent.sh +217 -0
- package/.agents/skills/backend-dev-guidelines/SKILL.md +352 -0
- package/.agents/skills/backend-dev-guidelines/resources/architecture-overview.md +451 -0
- package/.agents/skills/backend-dev-guidelines/resources/async-and-errors.md +307 -0
- package/.agents/skills/backend-dev-guidelines/resources/complete-examples.md +638 -0
- package/.agents/skills/backend-dev-guidelines/resources/configuration.md +275 -0
- package/.agents/skills/backend-dev-guidelines/resources/database-patterns.md +224 -0
- package/.agents/skills/backend-dev-guidelines/resources/middleware-guide.md +213 -0
- package/.agents/skills/backend-dev-guidelines/resources/routing-and-controllers.md +756 -0
- package/.agents/skills/backend-dev-guidelines/resources/sentry-and-monitoring.md +336 -0
- package/.agents/skills/backend-dev-guidelines/resources/services-and-repositories.md +789 -0
- package/.agents/skills/backend-dev-guidelines/resources/testing-guide.md +235 -0
- package/.agents/skills/backend-dev-guidelines/resources/validation-patterns.md +754 -0
- package/.agents/skills/backend-development/SKILL.md +156 -0
- package/.agents/skills/backend-patterns/SKILL.md +562 -0
- package/.agents/skills/brainstorming/SKILL.md +159 -0
- package/.agents/skills/brainstorming/scripts/frame-template.html +213 -0
- package/.agents/skills/brainstorming/scripts/helper.js +167 -0
- package/.agents/skills/brainstorming/scripts/server.cjs +723 -0
- package/.agents/skills/brainstorming/scripts/start-server.sh +209 -0
- package/.agents/skills/brainstorming/scripts/stop-server.sh +120 -0
- package/.agents/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/.agents/skills/brainstorming/visual-companion.md +291 -0
- package/.agents/skills/code-reviewer/SKILL.md +65 -0
- package/.agents/skills/content-strategy/SKILL.md +365 -0
- package/.agents/skills/content-strategy/evals/evals.json +90 -0
- package/.agents/skills/content-strategy/references/headless-cms.md +194 -0
- package/.agents/skills/deploy-to-vercel/SKILL.md +296 -0
- package/.agents/skills/deploy-to-vercel/resources/deploy-codex.sh +301 -0
- package/.agents/skills/deploy-to-vercel/resources/deploy.sh +301 -0
- package/.agents/skills/deployment-checklist/SKILL.md +119 -0
- package/.agents/skills/design/SKILL.md +170 -0
- package/.agents/skills/design-an-interface/SKILL.md +94 -0
- package/.agents/skills/expose-localhost/SKILL.md +211 -0
- package/.agents/skills/expose-localhost/references/TROUBLESHOOTING.md +27 -0
- package/.agents/skills/find-skills/SKILL.md +142 -0
- package/.agents/skills/frontend-ui-engineering/SKILL.md +328 -0
- package/.agents/skills/git-commit/SKILL.md +124 -0
- package/.agents/skills/git-ops/SKILL.md +53 -0
- package/.agents/skills/git-workflow/SKILL.md +167 -0
- package/.agents/skills/google-agents-cli-adk-code/SKILL.md +72 -0
- package/.agents/skills/google-agents-cli-adk-code/references/adk-python.md +937 -0
- package/.agents/skills/google-agents-cli-adk-code/references/adk-workflows.md +507 -0
- package/.agents/skills/google-agents-cli-workflow/SKILL.md +310 -0
- package/.agents/skills/google-agents-cli-workflow/references/commands.md +12 -0
- package/.agents/skills/google-agents-cli-workflow/references/internals.md +24 -0
- package/.agents/skills/google-agents-cli-workflow/references/samples.md +59 -0
- package/.agents/skills/google-agents-cli-workflow/references/spec-template.md +25 -0
- package/.agents/skills/google-agents-cli-workflow/references/terminology.md +11 -0
- package/.agents/skills/grill-me/SKILL.md +47 -0
- package/.agents/skills/grill-with-docs/SKILL.md +7 -0
- package/.agents/skills/human-checkpoint/SKILL.md +21 -0
- package/.agents/skills/image/SKILL.md +340 -0
- package/.agents/skills/image/evals/evals.json +89 -0
- package/.agents/skills/image/references/ai-image-prompting.md +229 -0
- package/.agents/skills/image-generation/SKILL.md +374 -0
- package/.agents/skills/imagegen/LICENSE.txt +201 -0
- package/.agents/skills/imagegen/SKILL.md +279 -0
- package/.agents/skills/imagegen/agents/openai.yaml +6 -0
- package/.agents/skills/imagegen/assets/imagegen-small.svg +5 -0
- package/.agents/skills/imagegen/assets/imagegen.png +0 -0
- package/.agents/skills/imagegen/references/cli.md +160 -0
- package/.agents/skills/imagegen/references/codex-network.md +33 -0
- package/.agents/skills/imagegen/references/image-api.md +49 -0
- package/.agents/skills/imagegen/references/prompting.md +98 -0
- package/.agents/skills/imagegen/references/sample-prompts.md +376 -0
- package/.agents/skills/imagegen/scripts/image_gen.py +926 -0
- package/.agents/skills/imagegen-frontend-web/SKILL.md +987 -0
- package/.agents/skills/laravel-security/SKILL.md +948 -0
- package/.agents/skills/laravel-specialist/SKILL.md +264 -0
- package/.agents/skills/laravel-specialist/references/eloquent.md +351 -0
- package/.agents/skills/laravel-specialist/references/livewire.md +512 -0
- package/.agents/skills/laravel-specialist/references/queues.md +423 -0
- package/.agents/skills/laravel-specialist/references/routing.md +362 -0
- package/.agents/skills/laravel-specialist/references/testing.md +522 -0
- package/.agents/skills/laravel-tdd/SKILL.md +675 -0
- package/.agents/skills/laravel-verification/SKILL.md +180 -0
- package/.agents/skills/modern-web-guidance/SKILL.md +114 -0
- package/.agents/skills/modern-web-guidance/guides/accessibility/accessibility.md +463 -0
- package/.agents/skills/modern-web-guidance/guides/accessibility/accessible-error-announcement.md +207 -0
- package/.agents/skills/modern-web-guidance/guides/built-in-ai/language-detection.md +89 -0
- package/.agents/skills/modern-web-guidance/guides/built-in-ai/language-model.md +189 -0
- package/.agents/skills/modern-web-guidance/guides/built-in-ai/prompt-api.md +189 -0
- package/.agents/skills/modern-web-guidance/guides/built-in-ai/summarizer.md +154 -0
- package/.agents/skills/modern-web-guidance/guides/built-in-ai/translator.md +162 -0
- package/.agents/skills/modern-web-guidance/guides/css/css.md +527 -0
- package/.agents/skills/modern-web-guidance/guides/css/highlight-text-ranges.md +128 -0
- package/.agents/skills/modern-web-guidance/guides/css-layout/css-layout.md +305 -0
- package/.agents/skills/modern-web-guidance/guides/forms/animated-select-picker.md +178 -0
- package/.agents/skills/modern-web-guidance/guides/forms/autofill-address-form.md +75 -0
- package/.agents/skills/modern-web-guidance/guides/forms/autofill-highlight-inputs.md +45 -0
- package/.agents/skills/modern-web-guidance/guides/forms/autofill-payment-form.md +125 -0
- package/.agents/skills/modern-web-guidance/guides/forms/autofill-sign-in-form.md +135 -0
- package/.agents/skills/modern-web-guidance/guides/forms/autofill-sign-up-form.md +145 -0
- package/.agents/skills/modern-web-guidance/guides/forms/brand-consistent-forms.md +255 -0
- package/.agents/skills/modern-web-guidance/guides/forms/branded-select-styling.md +114 -0
- package/.agents/skills/modern-web-guidance/guides/forms/custom-select-picker-layouts.md +103 -0
- package/.agents/skills/modern-web-guidance/guides/forms/form-fields-automatically-fit-contents.md +121 -0
- package/.agents/skills/modern-web-guidance/guides/forms/forms.md +372 -0
- package/.agents/skills/modern-web-guidance/guides/forms/required-field-feedback.md +214 -0
- package/.agents/skills/modern-web-guidance/guides/forms/rich-media-picker.md +132 -0
- package/.agents/skills/modern-web-guidance/guides/forms/select-menu-interaction.md +180 -0
- package/.agents/skills/modern-web-guidance/guides/forms/validate-input-after-interaction.md +258 -0
- package/.agents/skills/modern-web-guidance/guides/html/html.md +407 -0
- package/.agents/skills/modern-web-guidance/guides/passkeys/passkey-authentication.md +205 -0
- package/.agents/skills/modern-web-guidance/guides/passkeys/passkey-conditional-create.md +134 -0
- package/.agents/skills/modern-web-guidance/guides/passkeys/passkey-management.md +211 -0
- package/.agents/skills/modern-web-guidance/guides/passkeys/passkey-reauthentication.md +128 -0
- package/.agents/skills/modern-web-guidance/guides/passkeys/passkey-registration.md +181 -0
- package/.agents/skills/modern-web-guidance/guides/passkeys/passkeys.md +38 -0
- package/.agents/skills/modern-web-guidance/guides/performance/batch-analytics-events.md +154 -0
- package/.agents/skills/modern-web-guidance/guides/performance/break-up-long-tasks.md +60 -0
- package/.agents/skills/modern-web-guidance/guides/performance/calculate-total-foreground-time.md +69 -0
- package/.agents/skills/modern-web-guidance/guides/performance/conditional-async-dependencies.md +71 -0
- package/.agents/skills/modern-web-guidance/guides/performance/defer-rendering-heavy-content.md +106 -0
- package/.agents/skills/modern-web-guidance/guides/performance/defer-work-until-scroll-ends.md +78 -0
- package/.agents/skills/modern-web-guidance/guides/performance/deprioritize-background-fetches.md +37 -0
- package/.agents/skills/modern-web-guidance/guides/performance/detect-initial-visibility-state.md +99 -0
- package/.agents/skills/modern-web-guidance/guides/performance/efficient-background-processing.md +117 -0
- package/.agents/skills/modern-web-guidance/guides/performance/faster-spa-view-transitions.md +79 -0
- package/.agents/skills/modern-web-guidance/guides/performance/full-session-analytics.md +135 -0
- package/.agents/skills/modern-web-guidance/guides/performance/identify-heavy-scripts.md +69 -0
- package/.agents/skills/modern-web-guidance/guides/performance/identify-inp-causes.md +73 -0
- package/.agents/skills/modern-web-guidance/guides/performance/improve-next-page-load-performance.md +119 -0
- package/.agents/skills/modern-web-guidance/guides/performance/interactions-in-complex-layouts.md +78 -0
- package/.agents/skills/modern-web-guidance/guides/performance/optimize-image-priority.md +61 -0
- package/.agents/skills/modern-web-guidance/guides/performance/optimize-preload-priority.md +41 -0
- package/.agents/skills/modern-web-guidance/guides/performance/optimize-script-priority.md +38 -0
- package/.agents/skills/modern-web-guidance/guides/performance/performance.md +366 -0
- package/.agents/skills/modern-web-guidance/guides/performance/resolution-optimized-pseudo-elements.md +47 -0
- package/.agents/skills/modern-web-guidance/guides/performance/schedule-tasks-by-priority.md +59 -0
- package/.agents/skills/modern-web-guidance/guides/performance/sequence-distributed-events.md +76 -0
- package/.agents/skills/modern-web-guidance/guides/privacy/privacy.md +247 -0
- package/.agents/skills/modern-web-guidance/guides/security/security.md +350 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/adapt-scrollbar-to-contrast-preferences.md +74 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/anchor-positioning-tab-underline.md +95 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/animate-element-entry-exit.md +131 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/animate-to-from-top-layer.md +140 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/animate-to-intrinsic-sizes.md +127 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/apply-webgl-shaders.md +239 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/calculate-event-differentials.md +85 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/calculate-with-intrinsic-sizes.md +196 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/capture-location-agnostic-data.md +63 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/carousel-slide-effects.md +187 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/carousel-snap-highlights.md +136 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/child-state-based-styling.md +101 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/complex-shapes.md +73 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/component-specific-light-dark-theme.md +179 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/consistent-cross-document-transitions.md +226 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/content-based-styling.md +118 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/coordinate-global-events.md +79 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/cross-document-transitions.md +127 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/custom-button-actions.md +173 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/customize-scrollbar-color-and-thickness.md +59 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/dark-mode.md +295 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/declarative-dialog-popover-control.md +238 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/deliver-optimized-decorative-images.md +49 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/design-token-reactivity.md +174 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/directional-navigation-transitions.md +125 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/dynamic-sibling-animations.md +54 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/dynamic-sibling-styling.md +98 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/export-html-media-from-canvas.md +256 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/expose-canvas-content-to-browser-features.md +401 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/flicker-free-client-side-ab-testing.md +168 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/fluid-scaling.md +78 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/format-human-readable-durations.md +87 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/group-element-transitions.md +124 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/improve-text-layout-and-legibility.md +76 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/individual-transform-properties.md +92 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/interactive-content-in-3d-scenes.md +383 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/interactive-content-reveal.md +118 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/interest-triggered-action-previews.md +79 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/interest-triggered-tooltips.md +132 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/light-dismiss-a-dialog.md +84 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/manage-recurring-intervals.md +73 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/model-partial-time-concepts.md +96 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/move-dom-element-without-losing-state.md +57 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/navigation-drawer.md +357 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/overflow-clipping-control.md +180 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/parallax-scroll-effects.md +230 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/persistent-app-tours.md +112 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/persistent-toast-notifications.md +30 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/persistent-top-layer-ui.md +48 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/physics-based-easing.md +118 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/platform-controls-dismiss-dialog.md +38 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/position-aware-tooltips.md +154 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/precise-text-alignment.md +99 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/prevent-text-wrapping.md +62 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/pull-to-reveal.md +83 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/reduce-style-repetition.md +87 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/resilient-context-menus-and-nested-dropdowns.md +102 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/same-document-transitions.md +177 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/scroll-entry-exit-effects.md +173 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/scroll-position-aware-elements.md +128 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/scroll-progress-indicator.md +133 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/scroll-snap-realtime-feedback.md +153 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/scroll-snap-state-sync.md +93 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/scroll-target-on-load.md +56 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/scrollability-affordance-hints.md +124 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/scrollytelling.md +181 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/search-hidden-content.md +136 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/shaped-cutouts.md +146 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/shrinking-header-on-scroll.md +126 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/size-aware-styling.md +104 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/soft-edge-content-fade.md +62 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/stabilize-reactive-state.md +72 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/stack-drill-down.md +680 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/style-parent-with-has.md +199 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/support-global-calendar-systems.md +107 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/swipe-to-remove.md +527 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/visually-stable-font-fallbacks.md +49 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/visually-stable-mixed-fonts.md +61 -0
- package/.agents/skills/modern-web-guidance/guides/user-experience/visually-texture-content.md +76 -0
- package/.agents/skills/modern-web-guidance/guides/webmcp/agentic-forms.md +116 -0
- package/.agents/skills/modern-web-guidance/guides/webmcp/agentic-javascript-tools.md +116 -0
- package/.agents/skills/modern-web-guidance/guides/webmcp/webmcp.md +43 -0
- package/.agents/skills/nextjs-app-router-patterns/SKILL.md +114 -0
- package/.agents/skills/nextjs-app-router-patterns/references/details.md +428 -0
- package/.agents/skills/nextjs-developer/SKILL.md +145 -0
- package/.agents/skills/nextjs-developer/references/app-router.md +311 -0
- package/.agents/skills/nextjs-developer/references/data-fetching.md +482 -0
- package/.agents/skills/nextjs-developer/references/deployment.md +545 -0
- package/.agents/skills/nextjs-developer/references/server-actions.md +462 -0
- package/.agents/skills/nextjs-developer/references/server-components.md +384 -0
- package/.agents/skills/nextjs-supabase-auth/SKILL.md +313 -0
- package/.agents/skills/nodejs-backend-patterns/SKILL.md +45 -0
- package/.agents/skills/nodejs-backend-patterns/references/advanced-patterns.md +430 -0
- package/.agents/skills/nodejs-backend-patterns/references/details.md +599 -0
- package/.agents/skills/performance-engineer/SKILL.md +181 -0
- package/.agents/skills/project-requirements/SKILL.md +43 -0
- package/.agents/skills/qa/SKILL.md +130 -0
- package/.agents/skills/security-review/LICENSE +22 -0
- package/.agents/skills/security-review/SKILL.md +312 -0
- package/.agents/skills/security-review/infrastructure/docker.md +432 -0
- package/.agents/skills/security-review/languages/javascript.md +387 -0
- package/.agents/skills/security-review/languages/python.md +363 -0
- package/.agents/skills/security-review/references/api-security.md +519 -0
- package/.agents/skills/security-review/references/authentication.md +353 -0
- package/.agents/skills/security-review/references/authorization.md +372 -0
- package/.agents/skills/security-review/references/business-logic.md +443 -0
- package/.agents/skills/security-review/references/cryptography.md +329 -0
- package/.agents/skills/security-review/references/csrf.md +398 -0
- package/.agents/skills/security-review/references/data-protection.md +378 -0
- package/.agents/skills/security-review/references/deserialization.md +410 -0
- package/.agents/skills/security-review/references/error-handling.md +436 -0
- package/.agents/skills/security-review/references/file-security.md +457 -0
- package/.agents/skills/security-review/references/injection.md +259 -0
- package/.agents/skills/security-review/references/logging.md +433 -0
- package/.agents/skills/security-review/references/misconfiguration.md +435 -0
- package/.agents/skills/security-review/references/modern-threats.md +475 -0
- package/.agents/skills/security-review/references/ssrf.md +413 -0
- package/.agents/skills/security-review/references/supply-chain.md +405 -0
- package/.agents/skills/security-review/references/xss.md +336 -0
- package/.agents/skills/seo-aeo-best-practices/SKILL.md +37 -0
- package/.agents/skills/seo-aeo-best-practices/references/aeo-considerations.md +159 -0
- package/.agents/skills/seo-aeo-best-practices/references/eeat-principles.md +127 -0
- package/.agents/skills/seo-aeo-best-practices/references/structured-data.md +183 -0
- package/.agents/skills/seo-aeo-best-practices/references/technical-seo.md +188 -0
- package/.agents/skills/seo-geo/.claude-plugin/plugin.json +29 -0
- package/.agents/skills/seo-geo/SKILL.md +250 -0
- package/.agents/skills/seo-geo/examples/opc-skills-case-study.md +330 -0
- package/.agents/skills/seo-geo/references/geo-research.md +319 -0
- package/.agents/skills/seo-geo/references/google-docs-summary.md +65 -0
- package/.agents/skills/seo-geo/references/platform-algorithms.md +354 -0
- package/.agents/skills/seo-geo/references/schema-templates.md +517 -0
- package/.agents/skills/seo-geo/references/seo-checklist.md +249 -0
- package/.agents/skills/seo-geo/references/tools-and-apis.md +313 -0
- package/.agents/skills/seo-geo/scripts/autocomplete_ideas.py +74 -0
- package/.agents/skills/seo-geo/scripts/backlinks.py +37 -0
- package/.agents/skills/seo-geo/scripts/competitor_gap.py +80 -0
- package/.agents/skills/seo-geo/scripts/credential.py +12 -0
- package/.agents/skills/seo-geo/scripts/dataforseo_api.py +104 -0
- package/.agents/skills/seo-geo/scripts/domain_overview.py +44 -0
- package/.agents/skills/seo-geo/scripts/keyword_research.py +38 -0
- package/.agents/skills/seo-geo/scripts/related_keywords.py +84 -0
- package/.agents/skills/seo-geo/scripts/seo_audit.py +151 -0
- package/.agents/skills/seo-geo/scripts/serp_analysis.py +41 -0
- package/.agents/skills/shadcn-ui/README.md +248 -0
- package/.agents/skills/shadcn-ui/SKILL.md +326 -0
- package/.agents/skills/shadcn-ui/examples/auth-layout.tsx +177 -0
- package/.agents/skills/shadcn-ui/examples/data-table.tsx +313 -0
- package/.agents/skills/shadcn-ui/examples/form-pattern.tsx +177 -0
- package/.agents/skills/shadcn-ui/resources/component-catalog.md +481 -0
- package/.agents/skills/shadcn-ui/resources/customization-guide.md +516 -0
- package/.agents/skills/shadcn-ui/resources/migration-guide.md +463 -0
- package/.agents/skills/shadcn-ui/resources/setup-guide.md +412 -0
- package/.agents/skills/shadcn-ui/scripts/verify-setup.sh +134 -0
- package/.agents/skills/skill-creator/LICENSE.txt +202 -0
- package/.agents/skills/skill-creator/SKILL.md +485 -0
- package/.agents/skills/skill-creator/agents/analyzer.md +274 -0
- package/.agents/skills/skill-creator/agents/comparator.md +202 -0
- package/.agents/skills/skill-creator/agents/grader.md +223 -0
- package/.agents/skills/skill-creator/assets/eval_review.html +146 -0
- package/.agents/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/.agents/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/.agents/skills/skill-creator/references/schemas.md +430 -0
- package/.agents/skills/skill-creator/scripts/__init__.py +0 -0
- package/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/.agents/skills/skill-creator/scripts/generate_report.py +326 -0
- package/.agents/skills/skill-creator/scripts/improve_description.py +247 -0
- package/.agents/skills/skill-creator/scripts/package_skill.py +136 -0
- package/.agents/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/.agents/skills/skill-creator/scripts/run_eval.py +310 -0
- package/.agents/skills/skill-creator/scripts/run_loop.py +328 -0
- package/.agents/skills/skill-creator/scripts/utils.py +47 -0
- package/.agents/skills/stacks/SKILL.md +21 -0
- package/.agents/skills/stacks/django.md +187 -0
- package/.agents/skills/stacks/express.md +16 -0
- package/.agents/skills/stacks/fastapi.md +17 -0
- package/.agents/skills/stacks/laravel.md +18 -0
- package/.agents/skills/stacks/nextjs.md +20 -0
- package/.agents/skills/stacks/react-native.md +170 -0
- package/.agents/skills/supabase/CHANGELOG.md +35 -0
- package/.agents/skills/supabase/SKILL.md +135 -0
- package/.agents/skills/supabase/assets/feedback-issue-template.md +17 -0
- package/.agents/skills/supabase/references/skill-feedback.md +17 -0
- package/.agents/skills/supabase-postgres-best-practices/CHANGELOG.md +29 -0
- package/.agents/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/.agents/skills/supabase-postgres-best-practices/references/_contributing.md +170 -0
- package/.agents/skills/supabase-postgres-best-practices/references/_sections.md +39 -0
- package/.agents/skills/supabase-postgres-best-practices/references/_template.md +34 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +63 -0
- package/.agents/skills/task-contract/SKILL.md +29 -0
- package/.agents/skills/token-optimization/SKILL.md +40 -0
- package/.agents/skills/ui-designer/SKILL.md +76 -0
- package/.agents/skills/ui-ux-pro-max/SKILL.md +704 -0
- package/.agents/skills/ui-ux-pro-max/data/_sync_all.py +414 -0
- package/.agents/skills/ui-ux-pro-max/data/app-interface.csv +31 -0
- package/.agents/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.agents/skills/ui-ux-pro-max/data/colors.csv +162 -0
- package/.agents/skills/ui-ux-pro-max/data/design.csv +1776 -0
- package/.agents/skills/ui-ux-pro-max/data/draft.csv +1779 -0
- package/.agents/skills/ui-ux-pro-max/data/google-fonts.csv +1924 -0
- package/.agents/skills/ui-ux-pro-max/data/icons.csv +106 -0
- package/.agents/skills/ui-ux-pro-max/data/landing.csv +35 -0
- package/.agents/skills/ui-ux-pro-max/data/motion.csv +17 -0
- package/.agents/skills/ui-ux-pro-max/data/products.csv +162 -0
- package/.agents/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/angular.csv +51 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/laravel.csv +51 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/threejs.csv +54 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.agents/skills/ui-ux-pro-max/data/styles.csv +85 -0
- package/.agents/skills/ui-ux-pro-max/data/typography.csv +74 -0
- package/.agents/skills/ui-ux-pro-max/data/ui-reasoning.csv +162 -0
- package/.agents/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.agents/skills/ui-ux-pro-max/scripts/core.py +274 -0
- package/.agents/skills/ui-ux-pro-max/scripts/design_system.py +1329 -0
- package/.agents/skills/ui-ux-pro-max/scripts/search.py +127 -0
- package/.agents/skills/vercel-cli-with-tokens/SKILL.md +353 -0
- package/.agents/skills/vercel-composition-patterns/AGENTS.md +946 -0
- package/.agents/skills/vercel-composition-patterns/README.md +60 -0
- package/.agents/skills/vercel-composition-patterns/SKILL.md +89 -0
- package/.agents/skills/vercel-composition-patterns/metadata.json +11 -0
- package/.agents/skills/vercel-composition-patterns/rules/_sections.md +29 -0
- package/.agents/skills/vercel-composition-patterns/rules/_template.md +24 -0
- package/.agents/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/.agents/skills/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
- package/.agents/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/.agents/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/.agents/skills/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/.agents/skills/vercel-composition-patterns/rules/state-context-interface.md +191 -0
- package/.agents/skills/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/.agents/skills/vercel-composition-patterns/rules/state-lift-state.md +125 -0
- package/.agents/skills/vercel-optimize/AGENTS.md +48 -0
- package/.agents/skills/vercel-optimize/CONTRIBUTING.md +41 -0
- package/.agents/skills/vercel-optimize/README.md +91 -0
- package/.agents/skills/vercel-optimize/SKILL.md +322 -0
- package/.agents/skills/vercel-optimize/lib/auth-route.mjs +23 -0
- package/.agents/skills/vercel-optimize/lib/budget-summary.mjs +182 -0
- package/.agents/skills/vercel-optimize/lib/citations.mjs +139 -0
- package/.agents/skills/vercel-optimize/lib/cost-coverage.mjs +143 -0
- package/.agents/skills/vercel-optimize/lib/dedup-recs.mjs +325 -0
- package/.agents/skills/vercel-optimize/lib/deep-dive.mjs +350 -0
- package/.agents/skills/vercel-optimize/lib/display-labels.mjs +185 -0
- package/.agents/skills/vercel-optimize/lib/extract-claims.mjs +550 -0
- package/.agents/skills/vercel-optimize/lib/framework-support.mjs +67 -0
- package/.agents/skills/vercel-optimize/lib/gates/build-minutes-fanout.mjs +69 -0
- package/.agents/skills/vercel-optimize/lib/gates/cold-start.mjs +66 -0
- package/.agents/skills/vercel-optimize/lib/gates/contract.mjs +79 -0
- package/.agents/skills/vercel-optimize/lib/gates/cwv-poor.mjs +87 -0
- package/.agents/skills/vercel-optimize/lib/gates/external-api-slow.mjs +55 -0
- package/.agents/skills/vercel-optimize/lib/gates/hard-gates.mjs +73 -0
- package/.agents/skills/vercel-optimize/lib/gates/index.mjs +45 -0
- package/.agents/skills/vercel-optimize/lib/gates/isr-overrevalidation.mjs +62 -0
- package/.agents/skills/vercel-optimize/lib/gates/middleware-heavy.mjs +51 -0
- package/.agents/skills/vercel-optimize/lib/gates/observability-events-attribution.mjs +56 -0
- package/.agents/skills/vercel-optimize/lib/gates/platform-bot-protection.mjs +115 -0
- package/.agents/skills/vercel-optimize/lib/gates/platform-fluid-compute.mjs +83 -0
- package/.agents/skills/vercel-optimize/lib/gates/region-misconfig.mjs +64 -0
- package/.agents/skills/vercel-optimize/lib/gates/route-errors.mjs +80 -0
- package/.agents/skills/vercel-optimize/lib/gates/scanner-driven.mjs +122 -0
- package/.agents/skills/vercel-optimize/lib/gates/select-candidates.mjs +134 -0
- package/.agents/skills/vercel-optimize/lib/gates/slow-route.mjs +88 -0
- package/.agents/skills/vercel-optimize/lib/gates/types.d.ts +38 -0
- package/.agents/skills/vercel-optimize/lib/gates/uncached-route.mjs +93 -0
- package/.agents/skills/vercel-optimize/lib/gates/usage-spike-triage.mjs +121 -0
- package/.agents/skills/vercel-optimize/lib/grade-recommendation.mjs +155 -0
- package/.agents/skills/vercel-optimize/lib/impact-label.mjs +126 -0
- package/.agents/skills/vercel-optimize/lib/impact-magnitude.mjs +60 -0
- package/.agents/skills/vercel-optimize/lib/investigation-brief.mjs +610 -0
- package/.agents/skills/vercel-optimize/lib/observation-safety.mjs +174 -0
- package/.agents/skills/vercel-optimize/lib/project-facts.mjs +99 -0
- package/.agents/skills/vercel-optimize/lib/queries.mjs +315 -0
- package/.agents/skills/vercel-optimize/lib/reconcile-candidates.mjs +372 -0
- package/.agents/skills/vercel-optimize/lib/render-report.mjs +955 -0
- package/.agents/skills/vercel-optimize/lib/repo-root.mjs +86 -0
- package/.agents/skills/vercel-optimize/lib/route-normalize.mjs +220 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/bot-protection-certainty.mjs +38 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/cache-tag-invalidation-certainty.mjs +30 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/count-correct.mjs +52 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/function-duration-invocations.mjs +38 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/index.mjs +79 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/middleware-conflict.mjs +36 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/missing-citation.mjs +16 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/pre-release.mjs +74 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/rate-limit.mjs +67 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/rendering-mode-mislabel.mjs +38 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/undeclared-dep.mjs +78 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/vercel-directive-strip.mjs +37 -0
- package/.agents/skills/vercel-optimize/lib/sanitizers/window-units.mjs +32 -0
- package/.agents/skills/vercel-optimize/lib/scanners/cache-components-suspense-dedupe.mjs +109 -0
- package/.agents/skills/vercel-optimize/lib/scanners/edge-heavy-import.mjs +94 -0
- package/.agents/skills/vercel-optimize/lib/scanners/force-dynamic.mjs +42 -0
- package/.agents/skills/vercel-optimize/lib/scanners/headers-in-page.mjs +44 -0
- package/.agents/skills/vercel-optimize/lib/scanners/index.mjs +35 -0
- package/.agents/skills/vercel-optimize/lib/scanners/large-static-asset.mjs +92 -0
- package/.agents/skills/vercel-optimize/lib/scanners/max-age-without-s-maxage.mjs +42 -0
- package/.agents/skills/vercel-optimize/lib/scanners/middleware-broad-matcher.mjs +55 -0
- package/.agents/skills/vercel-optimize/lib/scanners/missing-cache-headers.mjs +90 -0
- package/.agents/skills/vercel-optimize/lib/scanners/prisma-include-tree.mjs +42 -0
- package/.agents/skills/vercel-optimize/lib/scanners/region-pin-in-config.mjs +88 -0
- package/.agents/skills/vercel-optimize/lib/scanners/source-maps-production.mjs +36 -0
- package/.agents/skills/vercel-optimize/lib/scanners/sveltekit-prerender-missing.mjs +43 -0
- package/.agents/skills/vercel-optimize/lib/scanners/turbo-force-bypass.mjs +129 -0
- package/.agents/skills/vercel-optimize/lib/scanners/unoptimized-image.mjs +113 -0
- package/.agents/skills/vercel-optimize/lib/scanners/use-cache-date-stamp.mjs +106 -0
- package/.agents/skills/vercel-optimize/lib/support-topics.mjs +355 -0
- package/.agents/skills/vercel-optimize/lib/throttle.mjs +273 -0
- package/.agents/skills/vercel-optimize/lib/util.mjs +17 -0
- package/.agents/skills/vercel-optimize/lib/vercel.mjs +864 -0
- package/.agents/skills/vercel-optimize/lib/verify-claim.mjs +1296 -0
- package/.agents/skills/vercel-optimize/lib/workspace-resolver.mjs +521 -0
- package/.agents/skills/vercel-optimize/metadata.json +14 -0
- package/.agents/skills/vercel-optimize/references/candidates.md +176 -0
- package/.agents/skills/vercel-optimize/references/data-collection.md +218 -0
- package/.agents/skills/vercel-optimize/references/docs-library.json +683 -0
- package/.agents/skills/vercel-optimize/references/doctrine.md +105 -0
- package/.agents/skills/vercel-optimize/references/observability-plus.md +108 -0
- package/.agents/skills/vercel-optimize/references/playbooks/README.md +53 -0
- package/.agents/skills/vercel-optimize/references/playbooks/ai-application.md +32 -0
- package/.agents/skills/vercel-optimize/references/playbooks/api-service.md +30 -0
- package/.agents/skills/vercel-optimize/references/playbooks/content-site.md +30 -0
- package/.agents/skills/vercel-optimize/references/playbooks/ecommerce.md +30 -0
- package/.agents/skills/vercel-optimize/references/playbooks/marketing.md +30 -0
- package/.agents/skills/vercel-optimize/references/playbooks/saas.md +31 -0
- package/.agents/skills/vercel-optimize/references/playbooks/sveltekit.md +75 -0
- package/.agents/skills/vercel-optimize/references/recommendations.md +203 -0
- package/.agents/skills/vercel-optimize/references/scanner-patterns.md +251 -0
- package/.agents/skills/vercel-optimize/references/scoring.md +205 -0
- package/.agents/skills/vercel-optimize/references/support-topics/README.md +46 -0
- package/.agents/skills/vercel-optimize/references/support-topics/astro-edge-middleware-scope.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/astro-output-mode-and-isr.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/auth-preserving-parallelization.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/bot-protection-product-guardrails.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/build-minutes-monorepo-fanout.md +23 -0
- package/.agents/skills/vercel-optimize/references/support-topics/cache-components-static-shell-boundaries.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/cache-components-suspense-dedupe-pitfall.md +23 -0
- package/.agents/skills/vercel-optimize/references/support-topics/cdn-cache-auth-safety.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/cold-start-initialization-bundle.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/core-web-vitals-client-bottlenecks.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/database-egress-pooling-region.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/dynamic-rendering-traps.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/external-api-critical-path-platform.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/external-api-critical-path.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/fast-data-transfer-payloads.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/fluid-compute-caveats.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/function-duration-io-and-after.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/function-invocation-reduction.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/function-region-misconfiguration-ttfb.md +23 -0
- package/.agents/skills/vercel-optimize/references/support-topics/image-optimization-cost-control.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/isr-revalidation-static-generation.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/middleware-proxy-edge-cost.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/next-fetch-revalidate-floor.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/next-font-cls-self-hosting.md +23 -0
- package/.agents/skills/vercel-optimize/references/support-topics/next-heavy-ui-lazy-load-boundaries.md +23 -0
- package/.agents/skills/vercel-optimize/references/support-topics/next-image-lcp-preload-sizes.md +23 -0
- package/.agents/skills/vercel-optimize/references/support-topics/next-route-handler-get-cache-defaults.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/next-script-third-party-strategy.md +23 -0
- package/.agents/skills/vercel-optimize/references/support-topics/nextjs-version-cache-semantics.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/not-found-catchall-request-waste.md +23 -0
- package/.agents/skills/vercel-optimize/references/support-topics/nuxt-route-rules-cache-isr.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/observability-events-cost-attribution.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/post-response-work-waituntil.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/route-error-durable-offload.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/route-error-runtime-limits.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/runtime-cache-reusable-data.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/sveltekit-isr-prerender-safety.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/sveltekit-split-cold-start-tradeoff.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/usage-spike-triage.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/use-cache-date-stamp-isr-write-amplifier.md +23 -0
- package/.agents/skills/vercel-optimize/references/support-topics/use-cache-remote-shared-origin-data.md +22 -0
- package/.agents/skills/vercel-optimize/references/support-topics/workflow-resumable-stream-routes.md +23 -0
- package/.agents/skills/vercel-optimize/references/verification.md +102 -0
- package/.agents/skills/vercel-optimize/references/voice.md +76 -0
- package/.agents/skills/vercel-optimize/scripts/budget-summary.mjs +56 -0
- package/.agents/skills/vercel-optimize/scripts/build-docs.mjs +74 -0
- package/.agents/skills/vercel-optimize/scripts/check-citations.mjs +81 -0
- package/.agents/skills/vercel-optimize/scripts/check-docs-fresh.mjs +93 -0
- package/.agents/skills/vercel-optimize/scripts/collect-signals.mjs +576 -0
- package/.agents/skills/vercel-optimize/scripts/collect-sub-agent-outputs.mjs +296 -0
- package/.agents/skills/vercel-optimize/scripts/deep-dive.mjs +319 -0
- package/.agents/skills/vercel-optimize/scripts/gate-investigations.mjs +166 -0
- package/.agents/skills/vercel-optimize/scripts/merge-signals.mjs +192 -0
- package/.agents/skills/vercel-optimize/scripts/prepare-investigation-brief.mjs +231 -0
- package/.agents/skills/vercel-optimize/scripts/reconcile-candidates.mjs +62 -0
- package/.agents/skills/vercel-optimize/scripts/render-report.mjs +437 -0
- package/.agents/skills/vercel-optimize/scripts/scan-codebase.mjs +313 -0
- package/.agents/skills/vercel-optimize/scripts/verify-and-regen.mjs +346 -0
- package/.agents/skills/vercel-optimize/scripts/verify-finding.mjs +19 -0
- package/.agents/skills/vercel-react-best-practices/AGENTS.md +3810 -0
- package/.agents/skills/vercel-react-best-practices/README.md +123 -0
- package/.agents/skills/vercel-react-best-practices/SKILL.md +149 -0
- package/.agents/skills/vercel-react-best-practices/metadata.json +15 -0
- package/.agents/skills/vercel-react-best-practices/rules/_sections.md +46 -0
- package/.agents/skills/vercel-react-best-practices/rules/_template.md +28 -0
- package/.agents/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md +56 -0
- package/.agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.agents/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/.agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-defer-await.md +82 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-analyzable-paths.md +63 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +60 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-request-idle-callback.md +105 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +149 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md +50 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/.agents/skills/vercel-react-native-skills/AGENTS.md +2897 -0
- package/.agents/skills/vercel-react-native-skills/README.md +165 -0
- package/.agents/skills/vercel-react-native-skills/SKILL.md +121 -0
- package/.agents/skills/vercel-react-native-skills/metadata.json +16 -0
- package/.agents/skills/vercel-react-native-skills/rules/_sections.md +86 -0
- package/.agents/skills/vercel-react-native-skills/rules/_template.md +28 -0
- package/.agents/skills/vercel-react-native-skills/rules/animation-derived-value.md +53 -0
- package/.agents/skills/vercel-react-native-skills/rules/animation-gesture-detector-press.md +95 -0
- package/.agents/skills/vercel-react-native-skills/rules/animation-gpu-properties.md +65 -0
- package/.agents/skills/vercel-react-native-skills/rules/design-system-compound-components.md +66 -0
- package/.agents/skills/vercel-react-native-skills/rules/fonts-config-plugin.md +71 -0
- package/.agents/skills/vercel-react-native-skills/rules/imports-design-system-folder.md +68 -0
- package/.agents/skills/vercel-react-native-skills/rules/js-hoist-intl.md +61 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-callbacks.md +44 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-function-references.md +132 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-images.md +53 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-inline-objects.md +97 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-item-expensive.md +94 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-item-memo.md +82 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-item-types.md +104 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-virtualize.md +67 -0
- package/.agents/skills/vercel-react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
- package/.agents/skills/vercel-react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
- package/.agents/skills/vercel-react-native-skills/rules/navigation-native-navigators.md +188 -0
- package/.agents/skills/vercel-react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
- package/.agents/skills/vercel-react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
- package/.agents/skills/vercel-react-native-skills/rules/react-state-dispatcher.md +91 -0
- package/.agents/skills/vercel-react-native-skills/rules/react-state-fallback.md +56 -0
- package/.agents/skills/vercel-react-native-skills/rules/react-state-minimize.md +65 -0
- package/.agents/skills/vercel-react-native-skills/rules/rendering-no-falsy-and.md +74 -0
- package/.agents/skills/vercel-react-native-skills/rules/rendering-text-in-text-component.md +36 -0
- package/.agents/skills/vercel-react-native-skills/rules/scroll-position-no-state.md +82 -0
- package/.agents/skills/vercel-react-native-skills/rules/state-ground-truth.md +80 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-expo-image.md +66 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-image-gallery.md +104 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-measure-views.md +78 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-menus.md +174 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-native-modals.md +77 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-pressable.md +61 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-safe-area-scroll.md +65 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-styling.md +87 -0
- package/.agents/skills/vercel-react-view-transitions/AGENTS.md +955 -0
- package/.agents/skills/vercel-react-view-transitions/README.md +42 -0
- package/.agents/skills/vercel-react-view-transitions/SKILL.md +320 -0
- package/.agents/skills/vercel-react-view-transitions/metadata.json +12 -0
- package/.agents/skills/vercel-react-view-transitions/references/css-recipes.md +242 -0
- package/.agents/skills/vercel-react-view-transitions/references/implementation.md +182 -0
- package/.agents/skills/vercel-react-view-transitions/references/nextjs.md +176 -0
- package/.agents/skills/vercel-react-view-transitions/references/patterns.md +262 -0
- package/.agents/skills/web-design-guidelines/SKILL.md +39 -0
- package/.agents/skills/workflow-metrics/SKILL.md +18 -0
- package/.agents/skills/writing-guidelines/SKILL.md +39 -0
- package/CODING_STANDARDS.md +49 -0
- package/LICENSE +21 -0
- package/README.md +556 -0
- package/VERSION +1 -0
- package/bin/devos.js +893 -0
- package/docs/.gitkeep +0 -0
- package/docs/ARCHITECTURE.md +114 -0
- package/docs/CHANGELOG.md +61 -0
- package/docs/CURRENT_STATE.md +39 -0
- package/docs/FIELD_REPORT_2026-08-20.md +130 -0
- package/docs/GETTING_STARTED.md +130 -0
- package/docs/LESSONS.md +24 -0
- package/docs/PRE_COMMIT_HOOK.md +152 -0
- package/docs/SLASH_COMMANDS.md +116 -0
- package/docs/TUTORIAL.md +75 -0
- package/package.json +54 -0
- package/scripts/smoke-test.js +167 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
# Stack Drill Down
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
A stack drill-down is a hierarchical navigation pattern, common in mobile apps, where activating a link pushes a new full-screen view on top of the previous one. The view's content is application-defined — a settings sub-page, a thread inside a feed, a folder inside a file browser, a detail page inside a gallery, etc. The user returns by swiping the current view off-screen to the right or by tapping a back button. Browser history stays in sync so the OS-level Back gesture, deep links, and forward/back navigation all work coherently.
|
|
6
|
+
|
|
7
|
+
This guide implements the stack as:
|
|
8
|
+
|
|
9
|
+
- A horizontal CSS scroll-snap container where each view is exactly one snap stop. Drilling down appends a new view and smooth-scrolls to it; the swipe-back gesture is handled natively by the browser, giving momentum, velocity, and interruption tracking for free with no pointer-event JavaScript.
|
|
10
|
+
- A `scrollsnapchange` event listener on the stack that fires when the snap target changes. This is used as the single source of truth for "the active view changed" — so swipe, click, programmatic scroll, and `popstate` paths all converge in one callback that updates `inert`, restores focus, prunes views the user swiped past, and reconciles browser history.
|
|
11
|
+
- A `pushState` / `popstate` integration so every drill-down adds a history entry, the OS-level Back gesture works, and deep links open directly into the right view.
|
|
12
|
+
- An (optional) scroll-driven `view()` animation on each view that produces a parallax + dim + shadow effect tied directly to the swipe gesture, so the visible motion is driven by the user's finger, not a tween.
|
|
13
|
+
|
|
14
|
+
This approach is preferred over JavaScript-driven `transform` animations because the snap mechanism gives the user direct gestural control of the panel's position (their finger drives it, not a tween) and matches the interaction patterns users expect from native mobile apps.
|
|
15
|
+
|
|
16
|
+
## Implementation
|
|
17
|
+
|
|
18
|
+
### 1. Markup
|
|
19
|
+
|
|
20
|
+
The static HTML is just an empty stack container; views are built in JavaScript and appended as the user navigates.
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<div class="Stack">
|
|
24
|
+
<!-- Intentionally empty. The initial view is appended by JavaScript
|
|
25
|
+
at init time (step 8). -->
|
|
26
|
+
</div>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Each view is a `.Stack-view` direct child of `.Stack` (the snap target) with a `.Stack-viewContent` wrapper inside it (where view content lives, and where the parallax transform applies — see step 2). At any moment the stack has one view per active history entry, left-to-right in drill-down order. After the user has drilled in two levels from the root the rendered DOM looks like this:
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<div class="Stack">
|
|
33
|
+
<!-- Root view. Has whatever content makes sense as the entry point
|
|
34
|
+
of this section of the app. No back button — there is nothing
|
|
35
|
+
behind the root in the stack. -->
|
|
36
|
+
<div class="Stack-view" inert>
|
|
37
|
+
<div class="Stack-viewContent">
|
|
38
|
+
<!-- Root content; includes <a href> links that drill in. -->
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<!-- First-level drill-down view. The user got here by activating a
|
|
43
|
+
link in the root view. -->
|
|
44
|
+
<div class="Stack-view" inert>
|
|
45
|
+
<div class="Stack-viewContent">
|
|
46
|
+
<header>
|
|
47
|
+
<!-- DO include a back button. The swipe gesture only works on
|
|
48
|
+
touch — keyboard and pointer users need an explicit control. -->
|
|
49
|
+
<button class="back" aria-label="Back"></button>
|
|
50
|
+
<!-- Title / breadcrumb / etc. -->
|
|
51
|
+
</header>
|
|
52
|
+
<main>
|
|
53
|
+
<!-- View content; may include further drill-down <a href> links. -->
|
|
54
|
+
</main>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<!-- Second-level drill-down view. Currently visible — no `inert`
|
|
59
|
+
attribute. Same shape as the first-level view. -->
|
|
60
|
+
<div class="Stack-view">
|
|
61
|
+
<div class="Stack-viewContent">
|
|
62
|
+
<header>
|
|
63
|
+
<button class="back" aria-label="Back"></button>
|
|
64
|
+
<!-- ... -->
|
|
65
|
+
</header>
|
|
66
|
+
<main><!-- ... --></main>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Notes:
|
|
73
|
+
|
|
74
|
+
- All views except the currently-visible one carry the `inert` attribute. This is applied/removed automatically by the `scrollsnapchange` handler in step 7 — do not set it from your view builders.
|
|
75
|
+
- Views the user swipes back past are removed from the DOM (also by step 7) so the stack never grows beyond `currentDepth + 1` children. They are rebuilt on demand from their cached URL paths if forward navigation returns to them.
|
|
76
|
+
|
|
77
|
+
### 2. Styles
|
|
78
|
+
|
|
79
|
+
#### The stack scroller
|
|
80
|
+
|
|
81
|
+
The stack is a horizontal grid where each child view is exactly the width of the container, with CSS scroll-snap enforcing one-view-per-snap. This is what gives the swipe-back gesture its native feel.
|
|
82
|
+
|
|
83
|
+
```css
|
|
84
|
+
.Stack {
|
|
85
|
+
/* Use dvh so the height tracks the dynamic viewport on mobile, where
|
|
86
|
+
the address bar can show/hide. svh would clip during the address bar
|
|
87
|
+
animation; vh leaks under it. */
|
|
88
|
+
height: 100dvh;
|
|
89
|
+
|
|
90
|
+
/* Lay views out left-to-right, each one full-width, so horizontal
|
|
91
|
+
scrolling moves between them one at a time. */
|
|
92
|
+
display: grid;
|
|
93
|
+
grid-auto-flow: column;
|
|
94
|
+
grid-auto-columns: 100%;
|
|
95
|
+
grid-template-rows: 100%;
|
|
96
|
+
overflow-x: auto;
|
|
97
|
+
|
|
98
|
+
/* `mandatory` guarantees the stack always settles fully on a view —
|
|
99
|
+
never half-way between two. */
|
|
100
|
+
scroll-snap-type: x mandatory;
|
|
101
|
+
/* Prevent the swipe-back gesture from chaining into the browser's
|
|
102
|
+
own history-back gesture (iOS, some Android) or the page's vertical
|
|
103
|
+
scroll. The user is navigating the stack, not the page. */
|
|
104
|
+
overscroll-behavior-x: none;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Hide the visual scrollbar — the snap and the parallax are the
|
|
108
|
+
affordances; a horizontal scrollbar would look out of place. */
|
|
109
|
+
.Stack::-webkit-scrollbar {
|
|
110
|
+
display: none;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* MANDATORY: Opt into smooth programmatic scrolling via CSS, gated on
|
|
114
|
+
prefers-reduced-motion. JS code calls scrollTo/scrollBy with
|
|
115
|
+
behavior: 'auto' which defers to this rule, so the OS-level reduced-
|
|
116
|
+
motion preference automatically downgrades to instant scrolling
|
|
117
|
+
without any per-call JS branching. */
|
|
118
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
119
|
+
.Stack {
|
|
120
|
+
scroll-behavior: smooth;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.Stack-view {
|
|
125
|
+
scroll-snap-align: start;
|
|
126
|
+
/* `always` prevents the user from blowing through more than one view
|
|
127
|
+
per gesture, so depth changes always happen one step at a time. */
|
|
128
|
+
scroll-snap-stop: always;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* MANDATORY: A separate inner element is required for the parallax
|
|
132
|
+
transform below. Applying transforms directly to the snap target
|
|
133
|
+
(.Stack-view) would feed back into the scroll container's snap
|
|
134
|
+
geometry and the scroller would jump mid-gesture. */
|
|
135
|
+
.Stack-viewContent {
|
|
136
|
+
width: 100%;
|
|
137
|
+
height: 100%;
|
|
138
|
+
background-color: #fff;
|
|
139
|
+
/* Each view scrolls its own content vertically, independent of the
|
|
140
|
+
stack's horizontal scroll. */
|
|
141
|
+
overflow-y: auto;
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### The "stack" effect (parallax / dim / shadow)
|
|
146
|
+
|
|
147
|
+
A scroll-driven `view(inline)` animation tracks each view's progress through the stack scroller and applies a parallax + dim to the exiting view, plus a shadow on incoming drill-down views so they read as "cards" stacking over the previous view.
|
|
148
|
+
|
|
149
|
+
```css
|
|
150
|
+
/* MANDATORY: Wrap the animation block in @supports. Browsers without
|
|
151
|
+
scroll-driven animations still parse the @keyframes and would
|
|
152
|
+
apply the `to` state as a static style, leaving every view
|
|
153
|
+
permanently transformed. The @supports gate confines the animation
|
|
154
|
+
to browsers where it actually animates. */
|
|
155
|
+
@supports (animation-timeline: view()) {
|
|
156
|
+
.Stack-viewContent {
|
|
157
|
+
/* view(inline) tracks this element's progress through its nearest
|
|
158
|
+
scrollable ancestor on the inline (x) axis. */
|
|
159
|
+
animation: parallax linear both;
|
|
160
|
+
animation-timeline: view(inline);
|
|
161
|
+
/* Only animate the EXIT phase — when this view is being covered
|
|
162
|
+
by a deeper one. During its own entry the view stays at rest,
|
|
163
|
+
so the fresh content is fully bright and in position throughout. */
|
|
164
|
+
animation-range: exit 0% exit 100%;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* Drill-down views (everything except the root) also get a shadow on
|
|
168
|
+
their left edge during the transition so they feel like cards
|
|
169
|
+
stacking over the previous view. */
|
|
170
|
+
.Stack-view:not(:first-child) .Stack-viewContent {
|
|
171
|
+
animation: parallax linear both, shadow-fade linear both;
|
|
172
|
+
animation-timeline: view(inline), view(inline);
|
|
173
|
+
/* parallax: only exit (the view sliding back as a deeper one comes in).
|
|
174
|
+
shadow-fade: entry through exit (visible the whole time the view is
|
|
175
|
+
transitioning, not when it's at rest). */
|
|
176
|
+
animation-range: exit 0% exit 100%, entry 0% exit 100%;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@keyframes parallax {
|
|
180
|
+
/* translateX(75%) and brightness(0.8) are examples — adjust to taste. */
|
|
181
|
+
to {
|
|
182
|
+
transform: translateX(75%);
|
|
183
|
+
filter: brightness(0.8);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
@keyframes shadow-fade {
|
|
188
|
+
/* Shadow ramps in during entry, holds across the middle of the
|
|
189
|
+
gesture, and ramps out during exit — so it's only visible while
|
|
190
|
+
the view is mid-transition, not when at rest. */
|
|
191
|
+
0%, 100% { box-shadow: 0 0 1.5rem #0000; }
|
|
192
|
+
25%, 75% { box-shadow: 0 0 1.5rem #0004; }
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
NOTE: This effect is popular in modern native stack applications and is a good starting point, but the exact visual effects can be customized to fit existing transition styles as needed.
|
|
198
|
+
|
|
199
|
+
### 3. Module state
|
|
200
|
+
|
|
201
|
+
The stack tracks four pieces of state in module scope:
|
|
202
|
+
|
|
203
|
+
```js
|
|
204
|
+
const stack = document.querySelector('.Stack');
|
|
205
|
+
|
|
206
|
+
// Reference to the root view DOM element. The Stack starts empty, so
|
|
207
|
+
// this is null until the root view is created — either at init (step 8,
|
|
208
|
+
// when the URL is '/') or later by synthesizeRootEntry() (when the user
|
|
209
|
+
// landed on a deep link and then navigates back). Held as a mutable
|
|
210
|
+
// reference because other code (the scrollsnapchange handler, init, etc.)
|
|
211
|
+
// uses identity comparisons against it.
|
|
212
|
+
let rootView = null;
|
|
213
|
+
|
|
214
|
+
// Tracks which element to restore focus to when the user swipes back
|
|
215
|
+
// into a previous view. Keyed by the view element itself so entries
|
|
216
|
+
// are garbage-collected automatically when the view is pruned.
|
|
217
|
+
const returnFocus = new WeakMap();
|
|
218
|
+
|
|
219
|
+
// Maps history depth -> {urlPath, view}. We MUST maintain this map
|
|
220
|
+
// ourselves because the History API does not expose state for entries
|
|
221
|
+
// other than the current one — so when a view is pruned on swipe-back,
|
|
222
|
+
// we still need to remember which URL it represented in case the user
|
|
223
|
+
// later forward-navigates back into it.
|
|
224
|
+
const entriesByDepth = new Map();
|
|
225
|
+
|
|
226
|
+
// Tracked manually because history.state on a popstate event tells us
|
|
227
|
+
// the destination depth but not where we came from. We need both to
|
|
228
|
+
// compute the direction (back vs forward) and the distance.
|
|
229
|
+
let currentDepth = 0;
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Plus three application-specific helpers — the only places where your app's routing and view rendering plug in:
|
|
233
|
+
|
|
234
|
+
```js
|
|
235
|
+
// Resolve a URL path to the data your app needs to render the
|
|
236
|
+
// corresponding drill-down view, or return null for paths this section
|
|
237
|
+
// of the app does not handle (the root path '/', external links,
|
|
238
|
+
// unknown routes). resolveUrl() is for drill-down routes only — the
|
|
239
|
+
// root view is rendered separately by createRootView() below.
|
|
240
|
+
function resolveUrl(urlPath) {
|
|
241
|
+
// Replace with your routing logic. For example, match `/view/:id`
|
|
242
|
+
// and look the id up in your app state.
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Build the root (home) view of the stack. Application-specific content;
|
|
246
|
+
// preserve the .Stack-view / .Stack-viewContent wrapper structure (the
|
|
247
|
+
// inner element is required for the parallax — see step 2) and DO NOT
|
|
248
|
+
// render a back button — the root view has nothing behind it in the
|
|
249
|
+
// stack.
|
|
250
|
+
function createRootView() {
|
|
251
|
+
const view = document.createElement('div');
|
|
252
|
+
view.className = 'Stack-view';
|
|
253
|
+
view.innerHTML = `
|
|
254
|
+
<div class="Stack-viewContent">
|
|
255
|
+
<!-- Root content. Include <a href> elements pointing at URL paths
|
|
256
|
+
that resolveUrl() accepts, to enable drill-down from here. -->
|
|
257
|
+
</div>
|
|
258
|
+
`;
|
|
259
|
+
return view;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Build a drill-down view DOM element from the resolved route data.
|
|
263
|
+
// Customize the inner content freely, but DO preserve the .Stack-view
|
|
264
|
+
// / .Stack-viewContent wrapper structure and DO include a back button
|
|
265
|
+
// (the swipe gesture only works on touch).
|
|
266
|
+
function createDrillDownView(routeData) {
|
|
267
|
+
const view = document.createElement('div');
|
|
268
|
+
view.className = 'Stack-view';
|
|
269
|
+
view.innerHTML = `
|
|
270
|
+
<div class="Stack-viewContent">
|
|
271
|
+
<header>
|
|
272
|
+
<button class="back" aria-label="Back"></button>
|
|
273
|
+
<!-- Title, breadcrumb, or other view chrome derived from
|
|
274
|
+
routeData. -->
|
|
275
|
+
</header>
|
|
276
|
+
<main>
|
|
277
|
+
<!-- View body, also from routeData. Include further <a href>
|
|
278
|
+
elements pointing at URL paths that resolveUrl() also
|
|
279
|
+
accepts, to enable additional drill-downs from this view. -->
|
|
280
|
+
</main>
|
|
281
|
+
</div>
|
|
282
|
+
`;
|
|
283
|
+
return view;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function getCurrentUrlPath() {
|
|
287
|
+
return location.pathname;
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### 4. Drill down
|
|
292
|
+
|
|
293
|
+
A drill-down does four things in this order: push a history entry, build the new view, append it, smooth-scroll to it. The `scrollsnapchange` handler (step 7) picks up from there once the snap settles.
|
|
294
|
+
|
|
295
|
+
```js
|
|
296
|
+
function drillDown(urlPath) {
|
|
297
|
+
const routeData = resolveUrl(urlPath);
|
|
298
|
+
if (!routeData) return;
|
|
299
|
+
|
|
300
|
+
const newDepth = currentDepth + 1;
|
|
301
|
+
// Push BEFORE creating the view so the URL is correct if anything
|
|
302
|
+
// observing history (analytics, etc.) reads it during view creation.
|
|
303
|
+
history.pushState({depth: newDepth}, '', urlPath);
|
|
304
|
+
|
|
305
|
+
// pushState truncates forward entries in real browser history;
|
|
306
|
+
// mirror that truncation in our depth map so we don't hold references
|
|
307
|
+
// to views the user can no longer reach.
|
|
308
|
+
for (const d of entriesByDepth.keys()) {
|
|
309
|
+
if (d >= newDepth) entriesByDepth.delete(d);
|
|
310
|
+
}
|
|
311
|
+
currentDepth = newDepth;
|
|
312
|
+
|
|
313
|
+
const newView = createDrillDownView(routeData);
|
|
314
|
+
stack.appendChild(newView);
|
|
315
|
+
entriesByDepth.set(newDepth, {urlPath, view: newView});
|
|
316
|
+
|
|
317
|
+
// Scroll one viewport-width to the right. behavior: 'auto' defers to
|
|
318
|
+
// the CSS `scroll-behavior` set in step 2, which is smooth unless
|
|
319
|
+
// prefers-reduced-motion is set. The snap container locks onto the
|
|
320
|
+
// new view; the scrollsnapchange listener (step 7) fires when the
|
|
321
|
+
// snap settles.
|
|
322
|
+
stack.scrollBy({left: stack.clientWidth, behavior: 'auto'});
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### 5. Click and back-button handling
|
|
327
|
+
|
|
328
|
+
Intercept link clicks inside the stack and convert them to drill-downs. Preserve modifier-key behavior so cmd/middle-click still opens the link in a new tab.
|
|
329
|
+
|
|
330
|
+
```js
|
|
331
|
+
stack.addEventListener('click', (e) => {
|
|
332
|
+
// Back button: defer to goBack() (defined below), which handles both
|
|
333
|
+
// the normal in-app case and the deep-link case.
|
|
334
|
+
if (e.target.closest('.back')) {
|
|
335
|
+
goBack();
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Drill-down link.
|
|
340
|
+
const link = e.target.closest('a');
|
|
341
|
+
if (!link || !stack.contains(link)) return;
|
|
342
|
+
// Let the browser handle modified clicks so users can open links
|
|
343
|
+
// in new tabs / windows. e.button !== 0 filters out middle-clicks.
|
|
344
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
|
|
345
|
+
|
|
346
|
+
const urlPath = new URL(link.href).pathname;
|
|
347
|
+
const parentView = link.closest('.Stack-view');
|
|
348
|
+
// If the URL isn't handled by this section of the app (resolveUrl
|
|
349
|
+
// returns null), fall through so the browser navigates normally.
|
|
350
|
+
if (!resolveUrl(urlPath) || !parentView) return;
|
|
351
|
+
|
|
352
|
+
e.preventDefault();
|
|
353
|
+
// Record which link the user activated so focus can be restored to
|
|
354
|
+
// it when they swipe (or click) back into this view.
|
|
355
|
+
returnFocus.set(parentView, link);
|
|
356
|
+
drillDown(urlPath);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// Going back is usually just history.back(), but there's an important
|
|
360
|
+
// edge case: when the user lands directly on a deep-linked URL, there
|
|
361
|
+
// is no in-app history entry behind it. Calling history.back() in that
|
|
362
|
+
// situation would take them out of the app entirely. MANDATORY: detect
|
|
363
|
+
// this case and synthesize a root entry instead, so an in-app Back from
|
|
364
|
+
// a deep link lands on the root view and the platform Back from there
|
|
365
|
+
// returns the user to where they came from.
|
|
366
|
+
function goBack() {
|
|
367
|
+
const atDeepLinkRoot = currentDepth === 0
|
|
368
|
+
&& entriesByDepth.get(0)?.view !== rootView;
|
|
369
|
+
if (atDeepLinkRoot) {
|
|
370
|
+
synthesizeRootEntry();
|
|
371
|
+
} else {
|
|
372
|
+
// history.back() fires popstate, which routes through
|
|
373
|
+
// updateFromHistoryState (step 6) and scrolls the stack — the
|
|
374
|
+
// same path a swipe-back converges on.
|
|
375
|
+
history.back();
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function synthesizeRootEntry() {
|
|
380
|
+
// Push a new history entry pointing at the root URL. This becomes the
|
|
381
|
+
// entry the user "came from"; the original deep-linked entry is now
|
|
382
|
+
// behind us, so platform Back from the root view will return there.
|
|
383
|
+
const newDepth = currentDepth + 1;
|
|
384
|
+
history.pushState({depth: newDepth}, '', '/');
|
|
385
|
+
|
|
386
|
+
// Create the root view if it doesn't exist yet (we landed on a deep
|
|
387
|
+
// link and never needed it before now), and insert it at the LEFT end
|
|
388
|
+
// of the stack. Adjust scrollLeft by one viewport width so the user's
|
|
389
|
+
// view doesn't visually jump — they should still be looking at the
|
|
390
|
+
// deep-linked view until the scroll animation below runs.
|
|
391
|
+
if (!rootView) {
|
|
392
|
+
rootView = createRootView();
|
|
393
|
+
stack.prepend(rootView);
|
|
394
|
+
stack.scrollLeft += stack.clientWidth;
|
|
395
|
+
}
|
|
396
|
+
entriesByDepth.set(newDepth, {urlPath: '/', view: rootView});
|
|
397
|
+
|
|
398
|
+
// Now scroll to the new entry (the root view). updateFromHistoryState
|
|
399
|
+
// smooth-scrolls one step left, the parallax plays, and
|
|
400
|
+
// scrollsnapchange fires when the root view settles.
|
|
401
|
+
updateFromHistoryState(history.state);
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### 6. Sync from history (popstate)
|
|
406
|
+
|
|
407
|
+
`popstate` fires when the user uses the browser/OS back or forward button, or when JavaScript calls `history.back()` / `.go()`. This handler is the only path that scrolls the stack in response to a history change.
|
|
408
|
+
|
|
409
|
+
```js
|
|
410
|
+
window.addEventListener('popstate', (event) => {
|
|
411
|
+
updateFromHistoryState(event.state);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
function updateFromHistoryState(state, behaviorOverride) {
|
|
415
|
+
const newDepth = state?.depth ?? 0;
|
|
416
|
+
const urlPath = getCurrentUrlPath();
|
|
417
|
+
|
|
418
|
+
// Ensure entriesByDepth has an entry for the destination depth.
|
|
419
|
+
// If the URL changed (e.g. forward-nav into a previously-pruned
|
|
420
|
+
// view), clear the cached view reference so the loop below rebuilds.
|
|
421
|
+
const entry = entriesByDepth.get(newDepth) ?? {view: null};
|
|
422
|
+
if (entry.urlPath !== urlPath) {
|
|
423
|
+
entry.urlPath = urlPath;
|
|
424
|
+
entry.view = urlPath === '/' ? rootView : null;
|
|
425
|
+
}
|
|
426
|
+
entriesByDepth.set(newDepth, entry);
|
|
427
|
+
|
|
428
|
+
// Rebuild any views between root and the destination that were
|
|
429
|
+
// pruned earlier (when the user swiped back past them). Without
|
|
430
|
+
// this, forward-navigating to a previously-pruned view would have
|
|
431
|
+
// no element to scroll to.
|
|
432
|
+
for (let d = 0; d <= newDepth; d++) {
|
|
433
|
+
const e = entriesByDepth.get(d);
|
|
434
|
+
if (!e || e.view) continue;
|
|
435
|
+
const routeData = resolveUrl(e.urlPath);
|
|
436
|
+
if (!routeData) continue;
|
|
437
|
+
const rebuilt = createDrillDownView(routeData);
|
|
438
|
+
stack.appendChild(rebuilt);
|
|
439
|
+
e.view = rebuilt;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
currentDepth = newDepth;
|
|
443
|
+
|
|
444
|
+
const targetView = entriesByDepth.get(newDepth)?.view;
|
|
445
|
+
if (!targetView) return;
|
|
446
|
+
|
|
447
|
+
// Compare destination index against current scroll position so we
|
|
448
|
+
// can bail if they're already aligned. This is reached when the
|
|
449
|
+
// scrollsnapchange handler below calls history.go() to sync history
|
|
450
|
+
// after a swipe-back that already completed visually — there's
|
|
451
|
+
// nothing more to scroll.
|
|
452
|
+
const toIdx = [...stack.children].indexOf(targetView);
|
|
453
|
+
const fromIdx = Math.round(stack.scrollLeft / stack.clientWidth);
|
|
454
|
+
if (fromIdx === toIdx) return;
|
|
455
|
+
|
|
456
|
+
// Pick a scroll behavior:
|
|
457
|
+
// - multi-step jumps (e.g. history.go(-3)): 'instant' to skip
|
|
458
|
+
// intermediate snap points — otherwise smooth-scrolling would
|
|
459
|
+
// fire scrollsnapchange for each one and do N rounds of
|
|
460
|
+
// state-transition work for no reason.
|
|
461
|
+
// - rightward (forward) single-step: 'instant'. Browser-forward is
|
|
462
|
+
// rare on the web and is often spurious (e.g. iOS Safari treats
|
|
463
|
+
// edge swipes as forward navigation, even with overscroll-behavior
|
|
464
|
+
// set). An instant swap reads as "snap" rather than a misleading
|
|
465
|
+
// drilldown animation the user didn't ask for. The user-initiated
|
|
466
|
+
// drill-down path (drillDown, step 4) is unaffected — it calls
|
|
467
|
+
// scrollBy directly and never reaches this code.
|
|
468
|
+
// - leftward (back) single-step: 'auto' so the CSS `scroll-behavior`
|
|
469
|
+
// (smooth unless prefers-reduced-motion is set — see step 2)
|
|
470
|
+
// applies. Back is the common, expected case and benefits from
|
|
471
|
+
// the animation.
|
|
472
|
+
// NOTE: "forward" here means spatial direction (toIdx > fromIdx),
|
|
473
|
+
// NOT depth direction. synthesizeRootEntry (step 5) pushes a new
|
|
474
|
+
// depth but scrolls LEFT to the root view, which correctly reads as
|
|
475
|
+
// back-style (smooth).
|
|
476
|
+
const forward = toIdx > fromIdx;
|
|
477
|
+
const multiStep = Math.abs(toIdx - fromIdx) > 1;
|
|
478
|
+
const behavior = behaviorOverride ?? (forward || multiStep ? 'instant' : 'auto');
|
|
479
|
+
stack.scrollTo({left: toIdx * stack.clientWidth, behavior});
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### 7. `scrollsnapchange`: the single source of truth
|
|
484
|
+
|
|
485
|
+
After every snap commit — whether triggered by a swipe, a click, or a programmatic scroll — the browser fires a `scrollsnapchange` event on the scroll container, with the newly snapped element exposed as `event.snapTargetInline` (for horizontal snapping). Putting all state transitions inside this one handler is what keeps the swipe path, the click path, and the `popstate` path coherent.
|
|
486
|
+
|
|
487
|
+
The handler is extracted into a standalone `onActiveViewChanged` function so the fallback (see "Fallback strategies" below) can reuse it without duplicating the logic.
|
|
488
|
+
|
|
489
|
+
```js
|
|
490
|
+
function onActiveViewChanged(currentView) {
|
|
491
|
+
// Walk the stack in DOM order to update each view's role:
|
|
492
|
+
// - Views at or before currentView stay in the DOM but get
|
|
493
|
+
// `inert` (except currentView) so focus, pointer events, and
|
|
494
|
+
// AT navigation cannot leak into views hidden behind the
|
|
495
|
+
// parallax.
|
|
496
|
+
// - Views after currentView are unreachable (the user swiped
|
|
497
|
+
// back past them) so we drop them from the DOM to free memory.
|
|
498
|
+
// Their urlPath stays in entriesByDepth so a later forward
|
|
499
|
+
// navigation can rebuild the view from scratch.
|
|
500
|
+
let seenCurrent = false;
|
|
501
|
+
for (const view of [...stack.children]) {
|
|
502
|
+
if (seenCurrent) {
|
|
503
|
+
for (const e of entriesByDepth.values()) {
|
|
504
|
+
if (e.view === view) e.view = null;
|
|
505
|
+
}
|
|
506
|
+
view.remove();
|
|
507
|
+
} else {
|
|
508
|
+
// MANDATORY: inert non-current views. Without this, tabbing
|
|
509
|
+
// and screen-reader navigation can reach content hidden behind
|
|
510
|
+
// the parallax — a severe accessibility failure that's
|
|
511
|
+
// invisible to sighted users.
|
|
512
|
+
view.toggleAttribute('inert', view !== currentView);
|
|
513
|
+
if (view === currentView) seenCurrent = true;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// If the visible view's depth doesn't match `currentDepth`, the
|
|
518
|
+
// user got here by swiping (not clicking) — sync history so the
|
|
519
|
+
// browser back/forward buttons stay coherent with what's on screen.
|
|
520
|
+
let currentViewDepth;
|
|
521
|
+
for (const [d, e] of entriesByDepth) {
|
|
522
|
+
if (e.view === currentView) currentViewDepth = d;
|
|
523
|
+
}
|
|
524
|
+
if (currentViewDepth !== undefined && currentViewDepth !== currentDepth) {
|
|
525
|
+
// history.go fires popstate, which re-enters updateFromHistoryState.
|
|
526
|
+
// That call's fromIdx === toIdx check bails out without scrolling.
|
|
527
|
+
history.go(currentViewDepth - currentDepth);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Restore focus on the now-active view:
|
|
531
|
+
// - If we recorded which link the user activated to drill out
|
|
532
|
+
// of this view, return focus there so a swipe-back lands them
|
|
533
|
+
// exactly where they left off.
|
|
534
|
+
// - Otherwise (a freshly-pushed drill-down view), move focus to
|
|
535
|
+
// the back button so keyboard users have an obvious next action.
|
|
536
|
+
// - preventScroll is REQUIRED: without it, .focus() scrolls the
|
|
537
|
+
// snap container to bring the focused element into view, which
|
|
538
|
+
// fights the snap and can land the user mid-snap.
|
|
539
|
+
const stored = returnFocus.get(currentView);
|
|
540
|
+
if (stored) {
|
|
541
|
+
stored.focus({preventScroll: true});
|
|
542
|
+
returnFocus.delete(currentView);
|
|
543
|
+
} else if (currentView !== rootView) {
|
|
544
|
+
currentView.querySelector('.back')?.focus({preventScroll: true});
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
stack.addEventListener('scrollsnapchange', (event) => {
|
|
549
|
+
// snapTargetInline is the element that was just snapped to on the
|
|
550
|
+
// inline (horizontal) axis. For this stack — where each view is one
|
|
551
|
+
// horizontal snap stop — that's the new active view.
|
|
552
|
+
onActiveViewChanged(event.snapTargetInline);
|
|
553
|
+
});
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### 8. Initialization (including deep links)
|
|
557
|
+
|
|
558
|
+
When the page loads, the URL may already point at a deep view (a shared link, a bookmark, a refresh on a deep page). Build whichever initial view matches the URL — root or deep-linked, but never both — append it to the empty stack, seed the depth-0 history entry, and run an initial scroll pass with `behavior: 'instant'` so the parallax doesn't animate on first paint.
|
|
559
|
+
|
|
560
|
+
```js
|
|
561
|
+
const initialUrlPath = getCurrentUrlPath();
|
|
562
|
+
const initialRouteData = resolveUrl(initialUrlPath);
|
|
563
|
+
|
|
564
|
+
// Build the initial view: a drill-down view if the URL maps to one,
|
|
565
|
+
// otherwise the root view. Whichever it is, that's the only view in
|
|
566
|
+
// the stack right now — the other will be created lazily by
|
|
567
|
+
// synthesizeRootEntry (step 5) or drillDown (step 4) if the user
|
|
568
|
+
// navigates to it.
|
|
569
|
+
let initialView;
|
|
570
|
+
if (initialRouteData) {
|
|
571
|
+
initialView = createDrillDownView(initialRouteData);
|
|
572
|
+
} else {
|
|
573
|
+
rootView = createRootView();
|
|
574
|
+
initialView = rootView;
|
|
575
|
+
}
|
|
576
|
+
stack.appendChild(initialView);
|
|
577
|
+
|
|
578
|
+
entriesByDepth.set(0, {urlPath: initialUrlPath, view: initialView});
|
|
579
|
+
// replaceState attaches a `depth` to the entry the user landed on, so
|
|
580
|
+
// any subsequent pushState / popstate has a base depth to count from.
|
|
581
|
+
history.replaceState({depth: 0}, '');
|
|
582
|
+
|
|
583
|
+
updateFromHistoryState(history.state, 'instant');
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### Best practices
|
|
587
|
+
|
|
588
|
+
- **DO** use the `scrollsnapchange` event (with an `IntersectionObserver` fallback — see "Fallback strategies") as the source of truth for "the active view changed", not scroll-event coordinates. Snap commit is the only event that fires consistently across swipe, click, programmatic scroll, and `popstate` paths.
|
|
589
|
+
- **DO** apply transforms to a child of the snap target, never to the snap target itself. A transform on the snap target feeds back into the scroll container's snap geometry and the scroller will glitch mid-gesture.
|
|
590
|
+
- **DO** apply `inert` to every view except the currently visible one. Without this, focus and screen-reader navigation leak into views hidden behind the parallax — invisible to sighted users but a severe accessibility failure.
|
|
591
|
+
- **DO** push a history entry on every drill-down and handle `popstate` so the OS-level Back gesture and the browser back/forward buttons work. This is what makes the pattern feel like a native app.
|
|
592
|
+
- **DO** reconcile history from the active-view-changed handler when a swipe-back lands on a view whose depth doesn't match `currentDepth`. Without this, a subsequent OS Back returns the user somewhere unexpected because the browser's history cursor is out of sync with what's on screen.
|
|
593
|
+
- **DO** prune views the user swiped past from the DOM. A long drill-down session can otherwise accumulate dozens of detached subtrees. The cached URL path in `entriesByDepth` is enough to rebuild any view if forward navigation returns to it.
|
|
594
|
+
- **DO** call `.focus({preventScroll: true})` when restoring focus inside the stack. The default `preventScroll: false` makes the browser scroll the focus target into view, which fights the snap container and can land the user mid-snap.
|
|
595
|
+
- **DO** preserve cmd/ctrl/middle-click on internal links so URLs remain shareable and openable in a new tab.
|
|
596
|
+
- **DO** use `behavior: 'instant'` for multi-step history jumps AND for spatial-forward popstate transitions (`toIdx > fromIdx`). Multi-step jumps would otherwise fire `scrollsnapchange` at every intermediate snap and do N rounds of inert/focus/history work. Forward popstates are often spurious — iOS Safari treats edge swipes as browser forward even with `overscroll-behavior-x: none` set, and an instant swap is much less misleading than animating a "drilldown" the user didn't initiate. The user-initiated drill-down path (`drillDown`) is unaffected because it scrolls directly, not via `popstate`.
|
|
597
|
+
- **DO** respect `prefers-reduced-motion`: declare `scroll-behavior: smooth` only inside `@media (prefers-reduced-motion: no-preference)` and call `scrollTo` / `scrollBy` with `behavior: 'auto'` (not `'smooth'`) so the OS-level preference takes effect without per-call JS branching. Hard-coding `behavior: 'smooth'` bypasses the user's setting.
|
|
598
|
+
- **DO** render real `<a href>` elements as drill-down triggers, not `<button onclick>` or `<div>`. Real anchors get URL preview on hover, shareability, middle-click, screen-reader role, and SEO for free.
|
|
599
|
+
- **DO** include an explicit back button in every drill-down view. The swipe gesture only works on touch — keyboard, pointer, and desktop users need a visible affordance.
|
|
600
|
+
- **DO NOT** call `history.pushState` from the `popstate` handler — that pushes *new* entries while the user is trying to go back and breaks the browser back button.
|
|
601
|
+
- **DO NOT** drive the parallax with a `scroll` event listener when scroll-driven animations are available. The CSS path runs on the compositor; a JS scroll listener runs on the main thread and will visibly drop frames during the gesture.
|
|
602
|
+
- **DO NOT** mutate views you removed from the DOM after a swipe-back. Treat `entriesByDepth` as the canonical record: a pruned entry has `view: null` and is rebuilt on demand in `updateFromHistoryState`.
|
|
603
|
+
|
|
604
|
+
### Fallback strategies
|
|
605
|
+
|
|
606
|
+
Most of the features used in this guide are Baseline Widely available, and do not require any fallback. The only features not widely available that may require fallbacks are scroll-snap-events and scroll-driven-animations, both of which have robust fallback or progressive enhancement stories, and are safe to use for this use case:
|
|
607
|
+
|
|
608
|
+
#### Scroll snap events
|
|
609
|
+
|
|
610
|
+
Scroll snap events has limited availability.
|
|
611
|
+
Supported by: Chrome 129 (Sep 2024) and Edge 129 (Sep 2024).
|
|
612
|
+
Unsupported in: Firefox and Safari.
|
|
613
|
+
|
|
614
|
+
The `scrollsnapchange` event is the cleanest way to detect "the active view changed" — one listener on the stack, fired exactly once per snap commit. In browsers without it, the same effect can be polyfilled with an `IntersectionObserver` watching each view for full visibility inside the stack. The fallback dispatches into the same `onActiveViewChanged` function the primary path uses, so all the state-transition logic stays in one place.
|
|
615
|
+
|
|
616
|
+
```js
|
|
617
|
+
// MANDATORY when supporting browsers that haven't shipped scroll-snap-events
|
|
618
|
+
// yet. Check `HTMLElement.prototype` (not `window` or `document`) — the
|
|
619
|
+
// event handler IDL attribute is added to the prototype when the feature
|
|
620
|
+
// is supported, regardless of whether any element has the handler set.
|
|
621
|
+
if (!('onscrollsnapchange' in HTMLElement.prototype)) {
|
|
622
|
+
const viewObserver = new IntersectionObserver((entries) => {
|
|
623
|
+
for (const entry of entries) {
|
|
624
|
+
// threshold:1 only fires for fully-visible entries, but the
|
|
625
|
+
// observer also emits a "leaving" entry per view that drops below
|
|
626
|
+
// ratio 1. Filter to the entering side, which is the snap-commit
|
|
627
|
+
// moment we're trying to detect.
|
|
628
|
+
if (entry.intersectionRatio === 1) {
|
|
629
|
+
onActiveViewChanged(entry.target);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}, {root: stack, threshold: 1});
|
|
633
|
+
|
|
634
|
+
// Auto-observe every .Stack-view as it's added to the stack, and stop
|
|
635
|
+
// observing as it's removed. Using a MutationObserver lets the primary
|
|
636
|
+
// code (drillDown, updateFromHistoryState, synthesizeRootEntry, init)
|
|
637
|
+
// stay free of fallback wiring.
|
|
638
|
+
new MutationObserver((mutations) => {
|
|
639
|
+
for (const m of mutations) {
|
|
640
|
+
for (const node of m.addedNodes) {
|
|
641
|
+
if (node.classList?.contains('Stack-view')) viewObserver.observe(node);
|
|
642
|
+
}
|
|
643
|
+
for (const node of m.removedNodes) {
|
|
644
|
+
if (node.classList?.contains('Stack-view')) viewObserver.unobserve(node);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}).observe(stack, {childList: true});
|
|
648
|
+
|
|
649
|
+
// Catch up to any views already in the stack at the time this code
|
|
650
|
+
// runs (typically the initial view appended in step 8).
|
|
651
|
+
for (const view of stack.children) viewObserver.observe(view);
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
#### Scroll-driven animations
|
|
656
|
+
|
|
657
|
+
Scroll-driven animations has limited availability.
|
|
658
|
+
Supported by: Chrome 115 (Jul 2023), Edge 115 (Jul 2023), and Safari 26 (Sep 2025).
|
|
659
|
+
Unsupported in: Firefox.
|
|
660
|
+
|
|
661
|
+
The scroll-driven parallax / dim / shadow effect is a progressive enhancement on top of the navigation core. The CSS `@supports (animation-timeline: view())` gate (shown in step 2) confines the animation to supporting browsers; everywhere else the views simply cut between snap stops with no transition. The component is fully functional without the parallax — snap, history sync, focus management, and `inert` all still work.
|
|
662
|
+
|
|
663
|
+
If a parallax fallback is required for older baseline targets, attach a `scroll` listener to the stack and write a CSS custom property describing each view's progress through the scrollport, then drive `transform` and `filter` from that property:
|
|
664
|
+
|
|
665
|
+
```js
|
|
666
|
+
if (!CSS.supports('animation-timeline: view()')) {
|
|
667
|
+
stack.addEventListener('scroll', () => {
|
|
668
|
+
const viewWidth = stack.clientWidth;
|
|
669
|
+
for (const view of stack.children) {
|
|
670
|
+
// Progress: 0 when this view is centered, 1 when it has fully
|
|
671
|
+
// exited to the left. Matches the @keyframes mapping above.
|
|
672
|
+
const offsetLeft = view.offsetLeft - stack.scrollLeft;
|
|
673
|
+
const progress = Math.min(1, Math.max(0, -offsetLeft / viewWidth));
|
|
674
|
+
const content = view.querySelector('.Stack-viewContent');
|
|
675
|
+
content.style.transform = `translateX(${progress * 75}%)`;
|
|
676
|
+
content.style.filter = `brightness(${1 - progress * 0.2})`;
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
```
|