feat: add per-rule icons and staggered scroll-in to Rules page

Claude-Session: https://claude.ai/code/session_01NhxA4npAXAxmCDEJt6kxSH
This commit is contained in:
belviskhoremk
2026-08-03 23:15:44 +00:00
parent 801c4c028f
commit 585c77ba42
4 changed files with 71 additions and 6 deletions

View File

@@ -0,0 +1,15 @@
import { render } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
import RuleIcon from './RuleIcon'
describe('RuleIcon', () => {
it('renders an svg for a known rule id', () => {
const { container } = render(<RuleIcon ruleId="deposit" />)
expect(container.querySelector('svg')).toBeInTheDocument()
})
it('falls back to a default icon for an unknown rule id without throwing', () => {
const { container } = render(<RuleIcon ruleId="not-a-real-rule" />)
expect(container.querySelector('svg path')).toBeInTheDocument()
})
})

View File

@@ -0,0 +1,33 @@
interface RuleIconProps {
ruleId: string
}
const ICON_PATHS: Record<string, string> = {
deposit: 'M4 7h16M4 7a2 2 0 00-2 2v8a2 2 0 002 2h16a2 2 0 002-2V9a2 2 0 00-2-2M4 7l2-4h12l2 4M9 13h1v3H9zM14 13h1v3h-1z',
cancellation: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z',
guests: 'M17 20h5v-2a4 4 0 00-3-3.87M9 20H4v-2a4 4 0 013-3.87m5-8.13a4 4 0 110 8 4 4 0 010-8zm6 4a4 4 0 10-3.2-6.4M5.2 5.6A4 4 0 108.4 12',
'hair-prep': 'M12 3c-2 3-5 5-5 9a5 5 0 0010 0c0-4-3-6-5-9zM12 12v9',
'health-safety': 'M12 2l8 4v6c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10V6l8-4z',
}
function RuleIcon({ ruleId }: RuleIconProps) {
const d = ICON_PATHS[ruleId] ?? ICON_PATHS.deposit
return (
<svg
width="28"
height="28"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d={d} />
</svg>
)
}
export default RuleIcon

View File

@@ -5,7 +5,11 @@
}
.section {
margin-bottom: var(--space-lg);
display: flex;
align-items: flex-start;
gap: var(--space-md);
margin-bottom: var(--space-md);
color: var(--color-gold);
}
.section h2 {
@@ -13,6 +17,12 @@
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 1px;
margin: 0 0 0.35rem;
}
.section p {
color: var(--color-charcoal);
margin: 0;
}
.cta {

View File

@@ -1,4 +1,6 @@
import { Link } from 'react-router-dom'
import ScrollReveal from '../components/ScrollReveal'
import RuleIcon from '../components/RuleIcon'
import { rules } from '../data/rules'
import styles from './Rules.module.css'
@@ -7,11 +9,16 @@ function Rules() {
<div className={styles.page}>
<h1>Salon Rules & Policies</h1>
<p>Please read through before booking your appointment.</p>
{rules.map((rule) => (
<div key={rule.id} className={styles.section}>
<h2>{rule.title}</h2>
<p>{rule.body}</p>
</div>
{rules.map((rule, index) => (
<ScrollReveal key={rule.id} delay={index * 0.08}>
<div className={styles.section}>
<RuleIcon ruleId={rule.id} />
<div>
<h2>{rule.title}</h2>
<p>{rule.body}</p>
</div>
</div>
</ScrollReveal>
))}
<div className={styles.cta}>
<p>By booking, you agree to these terms.</p>