From 585c77ba4260555439d36586f233f8fd65fea88f Mon Sep 17 00:00:00 2001 From: belviskhoremk Date: Mon, 3 Aug 2026 23:15:44 +0000 Subject: [PATCH] feat: add per-rule icons and staggered scroll-in to Rules page Claude-Session: https://claude.ai/code/session_01NhxA4npAXAxmCDEJt6kxSH --- src/components/RuleIcon.test.tsx | 15 +++++++++++++++ src/components/RuleIcon.tsx | 33 ++++++++++++++++++++++++++++++++ src/pages/Rules.module.css | 12 +++++++++++- src/pages/Rules.tsx | 17 +++++++++++----- 4 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 src/components/RuleIcon.test.tsx create mode 100644 src/components/RuleIcon.tsx diff --git a/src/components/RuleIcon.test.tsx b/src/components/RuleIcon.test.tsx new file mode 100644 index 0000000..677ecb2 --- /dev/null +++ b/src/components/RuleIcon.test.tsx @@ -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() + expect(container.querySelector('svg')).toBeInTheDocument() + }) + + it('falls back to a default icon for an unknown rule id without throwing', () => { + const { container } = render() + expect(container.querySelector('svg path')).toBeInTheDocument() + }) +}) diff --git a/src/components/RuleIcon.tsx b/src/components/RuleIcon.tsx new file mode 100644 index 0000000..746c484 --- /dev/null +++ b/src/components/RuleIcon.tsx @@ -0,0 +1,33 @@ +interface RuleIconProps { + ruleId: string +} + +const ICON_PATHS: Record = { + 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 ( + + ) +} + +export default RuleIcon diff --git a/src/pages/Rules.module.css b/src/pages/Rules.module.css index c22facd..ff20a19 100644 --- a/src/pages/Rules.module.css +++ b/src/pages/Rules.module.css @@ -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 { diff --git a/src/pages/Rules.tsx b/src/pages/Rules.tsx index 991d8be..b92f1b4 100644 --- a/src/pages/Rules.tsx +++ b/src/pages/Rules.tsx @@ -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() {

Salon Rules & Policies

Please read through before booking your appointment.

- {rules.map((rule) => ( -
-

{rule.title}

-

{rule.body}

-
+ {rules.map((rule, index) => ( + +
+ +
+

{rule.title}

+

{rule.body}

+
+
+
))}

By booking, you agree to these terms.