Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<title>Palengkatts</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Noto+Serif+Display:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600;700;800&family=Noto+Serif+Display:wght@400;500;600;700&display=swap" rel="stylesheet" />
</head>
<body>
<div id="root"></div>
Expand Down
6 changes: 4 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"clsx": "^2.1.1",
"framer-motion": "^12.42.2",
"leaflet": "^1.9.4",
"lucide-react": "^1.23.0",
"lucide-react": "0.469.0",
"react": "^18.3.1",
"react-day-picker": "^10.0.1",
"react-dom": "^18.3.1",
Expand Down
Binary file added public/images/qr/gcash-qr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/qr/maya-qr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 27 additions & 17 deletions src/components/Nav.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useState } from 'react'
import { Link, useLocation } from 'react-router-dom'
import { Menu, X } from 'lucide-react'
import { motion, AnimatePresence } from 'framer-motion'
import { mobileMenuContainer, mobileMenuItem } from '@/lib/motion'

const navLinks = [
{ to: '/', label: 'Markets' },
Expand Down Expand Up @@ -57,23 +59,31 @@ export default function Nav() {
</button>

{/* Mobile menu */}
{mobileOpen && (
<ul className="flex flex-col gap-3 py-4 w-full list-none md:hidden">
{navLinks.map(link => (
<li key={link.to}>
<Link
to={link.to}
className={`text-base font-medium py-2 block ${
location.pathname === link.to ? 'text-accent' : 'text-foreground-2'
}`}
onClick={() => setMobileOpen(false)}
>
{link.label}
</Link>
</li>
))}
</ul>
)}
<AnimatePresence>
{mobileOpen && (
<motion.ul
className="flex flex-col gap-3 py-4 w-full list-none md:hidden overflow-hidden"
variants={mobileMenuContainer}
initial="hidden"
animate="visible"
exit="exit"
>
{navLinks.map(link => (
<motion.li key={link.to} variants={mobileMenuItem}>
<Link
to={link.to}
className={`text-base font-medium py-2 block ${
location.pathname === link.to ? 'text-accent' : 'text-foreground-2'
}`}
onClick={() => setMobileOpen(false)}
>
{link.label}
</Link>
</motion.li>
))}
</motion.ul>
)}
</AnimatePresence>
</div>
</nav>
)
Expand Down
27 changes: 26 additions & 1 deletion src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,43 @@
--color-accent: #e8736d;
--color-accent-on: #ffffff;
--color-accent-soft: #fdf0ef;
--color-secondary: #d4a04a;
--color-secondary-on: #ffffff;
--color-secondary-soft: #fdf6e8;
--color-success: #2e9d57;
--color-warn: #ffb020;
--color-danger: #e5484d;
--color-info: #3b82f6;

/* Warm neutral scale */
--color-neutral-50: #faf8f5;
--color-neutral-100: #f5f0ec;
--color-neutral-200: #ebe4de;
--color-neutral-300: #d9d0c8;
--color-neutral-400: #b8aca2;
--color-neutral-500: #968a7e;
--color-neutral-600: #796f65;
--color-neutral-700: #5c5349;
--color-neutral-800: #3d3530;
--color-neutral-900: #1d1836;

--font-display: "Noto Serif Display", serif;
--font-body: Inter, system-ui, sans-serif;
--font-mono: "SF Mono", ui-monospace, Menlo, monospace;
--font-mono: "Geist Mono", "SF Mono", ui-monospace, Menlo, monospace;

--radius-sm: 10px;
--radius-md: 16px;
--radius-lg: 24px;
--radius-pill: 9999px;

/* Motion tokens */
--ease-enter: cubic-bezier(0.0, 0.0, 0.2, 1.0);
--ease-exit: cubic-bezier(0.4, 0.0, 1.0, 1.0);
--ease-move: cubic-bezier(0.4, 0.0, 0.2, 1.0);
--duration-micro: 100ms;
--duration-short: 200ms;
--duration-medium: 350ms;
--duration-long: 500ms;
}

@layer base {
Expand Down
23 changes: 19 additions & 4 deletions src/layouts/PublicLayout.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { Outlet } from 'react-router-dom'
import { Outlet, useLocation } from 'react-router-dom'
import { motion, AnimatePresence, MotionConfig } from 'framer-motion'
import Nav from '../components/Nav'
import Footer from '../components/Footer'
import { pageVariants, easeEnter, easeExit } from '../lib/motion'

export default function PublicLayout() {
const location = useLocation()

return (
<>
<MotionConfig reducedMotion="user">
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-[9999] focus:px-4 focus:py-2 focus:bg-accent focus:text-accent-on focus:rounded-full focus:text-sm focus:font-semibold focus:outline-none"
Expand All @@ -13,9 +17,20 @@ export default function PublicLayout() {
</a>
<Nav />
<div id="main-content">
<Outlet />
<AnimatePresence mode="wait">
<motion.div
key={location.pathname}
initial="initial"
animate="animate"
exit="exit"
variants={pageVariants}
transition={easeEnter}
>
<Outlet />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Snapshot the routed outlet before animating it

On any public route change, the exiting motion.div still renders a live <Outlet />. Because <Outlet /> reads the current router context, the destination route is mounted inside the exiting wrapper and then mounted again when mode="wait" admits the entering wrapper, so pages with mount effects such as Home and Adoption can refetch and briefly render under the exit animation. Store const outlet = useOutlet() and render that captured element inside the keyed motion node.

Useful? React with 👍 / 👎.

</motion.div>
</AnimatePresence>
</div>
<Footer />
</>
</MotionConfig>
)
}
206 changes: 206 additions & 0 deletions src/lib/motion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/**
* Palengkatts — Motion Design Tokens
* Reusable Framer Motion variants and transition presets.
* All animations respect prefers-reduced-motion via the MotionConfig in App.jsx.
*/

// --- Transitions ---

export const springGentle = {
type: 'spring',
stiffness: 200,
damping: 28,
}

export const springBouncy = {
type: 'spring',
stiffness: 300,
damping: 24,
}

export const easeEnter = {
duration: 0.35,
ease: [0.0, 0.0, 0.2, 1.0],
}

export const easeExit = {
duration: 0.2,
ease: [0.4, 0.0, 1.0, 1.0],
}

// --- Page Transitions ---

export const pageVariants = {
initial: { opacity: 0, y: 8 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -4 },
}

export const pageTransition = {
initial: 'initial',
animate: 'animate',
exit: 'exit',
variants: pageVariants,
transition: easeEnter,
}

// --- Staggered Container ---

export const staggerContainer = (staggerDelay = 0.06) => ({
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: staggerDelay,
delayChildren: 0.1,
},
},
})

// --- Staggered Items ---

export const staggerItem = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: springGentle,
},
}

// --- Hero Text Reveal ---

export const heroContainer = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.08,
delayChildren: 0.2,
},
},
}

export const heroLine = {
hidden: { opacity: 0, y: 24 },
visible: {
opacity: 1,
y: 0,
transition: springGentle,
},
}

// --- Scroll Reveal (for whileInView) ---

export const scrollReveal = {
hidden: { opacity: 0, y: 30 },
visible: {
opacity: 1,
y: 0,
transition: { ...easeEnter, duration: 0.5 },
},
}

// --- Card Hover ---

export const cardHover = {
rest: {
y: 0,
boxShadow: '0 0 0 1px var(--color-border)',
},
hover: {
y: -3,
boxShadow: '0 12px 32px rgba(232, 115, 109, 0.12)',
transition: { duration: 0.25, ease: [0.0, 0.0, 0.2, 1.0] },
},
}

// --- Button Press ---

export const buttonTap = {
scale: 0.97,
transition: { duration: 0.1 },
}

// --- Filter Chip ---

export const chipTap = {
scale: 0.95,
transition: springBouncy,
}

// --- Dialog ---

export const dialogOverlay = {
initial: { opacity: 0 },
animate: { opacity: 1 },
exit: { opacity: 0 },
transition: { duration: 0.2 },
}

export const dialogContent = {
initial: { opacity: 0, scale: 0.95, y: 8 },
animate: {
opacity: 1,
scale: 1,
y: 0,
transition: springBouncy,
},
exit: {
opacity: 0,
scale: 0.97,
y: 4,
transition: easeExit,
},
}

// --- Mobile Menu ---

export const mobileMenuContainer = {
hidden: { opacity: 0, height: 0 },
visible: {
opacity: 1,
height: 'auto',
transition: {
height: { duration: 0.3, ease: [0.0, 0.0, 0.2, 1.0] },
opacity: { duration: 0.2, delay: 0.05 },
staggerChildren: 0.04,
delayChildren: 0.1,
},
},
exit: {
opacity: 0,
height: 0,
transition: {
height: { duration: 0.2, ease: [0.4, 0.0, 1.0, 1.0] },
opacity: { duration: 0.15 },
},
},
}

export const mobileMenuItem = {
hidden: { opacity: 0, x: -12 },
visible: {
opacity: 1,
x: 0,
transition: springGentle,
},
}

// --- Fade In (generic utility) ---

export const fadeIn = (delay = 0) => ({
initial: { opacity: 0 },
animate: { opacity: 1, transition: { duration: 0.3, delay } },
})

// --- Scale In (for badges, toasts) ---

export const scaleIn = {
initial: { opacity: 0, scale: 0.85 },
animate: {
opacity: 1,
scale: 1,
transition: springBouncy,
},
}
Loading
Loading