You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
4.9 KiB

Nexo desktop-first redesign + demo restructure Move servers/dating → demos/dating/server and the dating route's co-located _components/_lib/_design assets → demos/dating/web. Single `npm run dating:dev` boots both server and web via concurrently. Replace the warm-paper / raspberry-pink Claude Design adaptation with a sober desktop-first system: - Tokens: slate neutrals + violet accent, Inter family, dark mode via prefers-color-scheme + explicit data-theme. - Inline SVG icon set (Icon.svelte, Feather-style) — no Unicode glyph placeholders. - Layout shell: 240px rail nav on desktop, bottom tab bar on mobile; auth + onboarding own their own shell. - Discover: square photo carousel (dots + chevrons), name/age/location overlay, action buttons floating off the photo edge, sticky context panel on desktop. - Matches: clean conversation list with unread dot + chevron-on-hover. - Chat: 3-column on desktop (threads + thread + match context), 2-col on tablet, single thread on mobile. Day separators, retry/dismiss on failed messages, autogrow composer with Enter-to-send. - Profile: sticky hero card + 3-section form, dirty/saved indicator. - Photos: dropzone + grid with hover-only cell toolbar (set primary, reorder, delete). - Onboarding: sidebar stepper (320px) + content panel. - Auth: split layout (form left, brand quote right) on ≥900px. Server boot: env loader's repo-root resolution corrected after the move (../../.. instead of ../..) so .env.local at the repo root is picked up again. Hoist BRAND.md and SECURITY.md into docs/, drop superseded audit notes that were already closed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
<!--
Inline SVG icon set, Feather-style (24×24, currentColor stroke,
1.75 stroke-width). One file so the bundle ships only the paths
we actually use. Add new entries to PATHS only when a route
references them.
-->
<script lang="ts" module>
export type IconName =
| 'arrow-left'
| 'arrow-right'
| 'send'
| 'x'
| 'check'
| 'heart'
| 'heart-fill'
| 'star'
| 'sparkle'
| 'message'
| 'user'
| 'shield'
| 'log-out'
| 'refresh'
| 'camera'
| 'image-plus'
| 'edit'
| 'map-pin'
| 'alert'
| 'info'
| 'eye'
| 'eye-off'
| 'chevron-left'
| 'chevron-right'
| 'sliders'
| 'plus'
| 'trash'
| 'arrow-up'
| 'arrow-down'
| 'more';
</script>
<script lang="ts">
interface Props {
name: IconName;
size?: number;
strokeWidth?: number;
fill?: boolean;
title?: string;
class?: string;
}
let {
name,
size = 20,
strokeWidth = 1.75,
fill = false,
title,
class: className = ''
}: Props = $props();
const PATHS: Record<IconName, { paths: string[]; viewBox?: string; fill?: boolean }> = {
'arrow-left': { paths: ['M19 12H5', 'M12 19l-7-7 7-7'] },
'arrow-right': { paths: ['M5 12h14', 'M12 5l7 7-7 7'] },
send: { paths: ['M22 2L11 13', 'M22 2l-7 20-4-9-9-4 20-7z'] },
x: { paths: ['M18 6L6 18', 'M6 6l12 12'] },
check: { paths: ['M20 6L9 17l-5-5'] },
heart: {
paths: [
'M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z'
]
},
'heart-fill': {
paths: [
'M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z'
],
fill: true
},
star: {
paths: ['M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z']
},
sparkle: {
paths: [
'M12 3v3',
'M12 18v3',
'M3 12h3',
'M18 12h3',
'M5.6 5.6l2.1 2.1',
'M16.3 16.3l2.1 2.1',
'M5.6 18.4l2.1-2.1',
'M16.3 7.7l2.1-2.1'
]
},
message: {
paths: ['M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z']
},
user: {
paths: ['M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2', 'M12 7a4 4 0 1 0 0 8 4 4 0 0 0 0-8z']
},
shield: { paths: ['M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z'] },
'log-out': {
paths: ['M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4', 'M16 17l5-5-5-5', 'M21 12H9']
},
refresh: {
paths: [
'M23 4v6h-6',
'M1 20v-6h6',
'M3.51 9a9 9 0 0 1 14.85-3.36L23 10',
'M20.49 15a9 9 0 0 1-14.85 3.36L1 14'
]
},
camera: {
paths: [
'M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z',
'M12 17a4 4 0 1 0 0-8 4 4 0 0 0 0 8z'
]
},
'image-plus': {
paths: [
'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9',
'M19 2v6',
'M22 5h-6',
'M21 15l-5-5L5 21'
]
},
edit: { paths: ['M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7', 'M18.5 2.5a2.121 2.121 0 1 1 3 3L12 15l-4 1 1-4 9.5-9.5z'] },
'map-pin': {
paths: ['M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 1 1 18 0z', 'M12 13a3 3 0 1 0 0-6 3 3 0 0 0 0 6z']
},
alert: { paths: ['M12 9v4', 'M12 17h.01', 'M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z'] },
info: { paths: ['M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z', 'M12 16v-4', 'M12 8h.01'] },
eye: { paths: ['M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z', 'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z'] },
'eye-off': {
paths: [
'M17.94 17.94A10.94 10.94 0 0 1 12 20c-7 0-11-8-11-8a19.79 19.79 0 0 1 5.06-5.94',
'M9.9 4.24A10.94 10.94 0 0 1 12 4c7 0 11 8 11 8a19.79 19.79 0 0 1-3.36 4.49',
'M14.12 14.12a3 3 0 1 1-4.24-4.24',
'M1 1l22 22'
]
},
'chevron-left': { paths: ['M15 18l-6-6 6-6'] },
'chevron-right': { paths: ['M9 18l6-6-6-6'] },
sliders: {
paths: [
'M4 21v-7',
'M4 10V3',
'M12 21v-9',
'M12 8V3',
'M20 21v-5',
'M20 12V3',
'M1 14h6',
'M9 8h6',
'M17 16h6'
]
},
plus: { paths: ['M12 5v14', 'M5 12h14'] },
trash: { paths: ['M3 6h18', 'M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6', 'M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2'] },
'arrow-up': { paths: ['M12 19V5', 'M5 12l7-7 7 7'] },
'arrow-down': { paths: ['M12 5v14', 'M19 12l-7 7-7-7'] },
more: { paths: ['M12 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2z', 'M19 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2z', 'M5 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2z'] }
};
const entry = $derived(PATHS[name]);
const useFill = $derived(fill || entry.fill === true);
</script>
<svg
class={className}
width={size}
height={size}
viewBox={entry.viewBox ?? '0 0 24 24'}
fill={useFill ? 'currentColor' : 'none'}
stroke="currentColor"
stroke-width={strokeWidth}
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden={title === undefined ? 'true' : undefined}
role={title === undefined ? undefined : 'img'}
>
{#if title}<title>{title}</title>{/if}
{#each entry.paths as d (d)}
<path {d} />
{/each}
</svg>

Powered by TurnKey Linux.