MVP ‐ UX design - bounswe/bounswe2026group4 GitHub Wiki
UX Design with Focus on Domain-Specific Nature
Our UX design is driven by the domain-specific nature of historical storytelling tied to geographic locations. Every design decision reflects the core concept: stories are inseparable from the places where they happened.
Map as Primary Interface
The map is the central interface element, as mandated by customer feedback. On the web, a full-viewport Leaflet map (calc(100vh - 3.5rem)) with OpenStreetMap tiles serves as the primary discovery surface. Each story appears as a pin marker at its geographic coordinates. Clicking a pin opens a popup showing the story title, location name, time period, and a "Read more" link — giving users enough context to decide whether to explore further without leaving the map.
On mobile, the map is rendered via react-native-maps inside a rounded card (420px height, borderRadius 24). The mobile version adds marker grouping/clustering — when multiple stories exist at nearby coordinates, they are grouped with a count indicator. Tapping a cluster shows a scrollable preview list of stories within, while tapping a single marker shows a full preview card with a "Read full story" button.
A domain-specific design choice: the map defaults to Istanbul ([41.0082, 28.9784]) as the initial view, reflecting the project's origin in a Boğaziçi University course and the Hisarüstü neighborhood data used for initial population.
The search/filter overlay on web uses a frosted glass effect (bg-background/95 backdrop-blur-sm) positioned in the top-left corner, ensuring the map remains visible while filtering. On mobile, filters open as a full-screen modal overlay to maximize input space on smaller screens.
Domain-Specific Time Input
Historical memories are inherently imprecise — a user might remember "it was in the 1960s" or "around 1985" rather than an exact date. The platform addresses this with four time resolution types:
| Time Type | Input | Display Format | Example |
|---|---|---|---|
| Exact Year | Single year field | "1453" |
The fall of Constantinople |
| Approximate Year | Single year field | "c. 1453" (with "circa" prefix) |
"I think it was around 1453" |
| Decade | Single year field | "1450s" |
"It was in the 1450s" |
| Year Range | Two fields (start/end) | "1400–1500" (en-dash) |
A period spanning multiple years |
This formatting is consistent across all surfaces (map popups, feed cards, story detail, search results) via a shared formatTimePeriod() utility.
Story Submission Flow
The submission form is designed to capture all domain-critical metadata in a single page:
- Title & Narrative — standard text inputs for the story content.
- Interactive Map Picker — a 300px Leaflet map where users click to place a pin at the story's location. Guidance text reads "Click on the map to select a location" until a pin is placed, then shows the selected coordinates. This ensures every story is geographically anchored.
- Place Name — a text field for the human-readable location name (e.g., "Hagia Sophia"), separate from coordinates.
- Time Period — a dropdown selects the time type, then conditional year inputs appear. Year Range shows two side-by-side inputs with validation ensuring start < end.
- Tags — 8 predefined domain-specific tags (Architecture, War, Culture, Trade, Religion, Daily Life, Art, Politics) shown as checkboxes. Maximum 3 tags enforced — additional checkboxes are disabled when the limit is reached. On mobile, tags are rendered as pill-shaped buttons with custom tag creation support.
- Image Upload — file input accepting JPEG/PNG (max 2MB) with inline preview. On mobile, users can choose from gallery or capture with camera.
- Contributor Visibility — a toggle allowing anonymous posting, respecting users who may share sensitive community stories.
The two-step submission (create story → upload image) ensures the story is saved even if the image upload fails, with a toast warning the user.
Feed View
The web feed uses a responsive grid layout (1 column on mobile, 2 at 640px, 3 at 1024px). Each story card shows:
- Title (capped to 2 lines with
line-clamp-2) with a heart icon (filled red when liked) - Contributor name with user icon
- Location with
MapPinicon and time period withCalendaricon - Preview text (capped to 3 lines)
The feed supports page-based pagination (12 stories per page) with URL-driven page state. Empty states show contextual messaging — "No stories yet" when the feed is truly empty vs. "No results found" when filters return nothing.
On mobile, the feed uses infinite scroll (FlatList with onEndReached) and pull-to-refresh. Cards include a media badge indicator ("IMG" pill) and time/date pills with rounded styling. The map and feed are connected via a horizontal swipe pager — users can swipe between map and feed views seamlessly.
Search and Filter System
Searching is designed around how users think about historical content — by place, time, and topic:
- Search bar: Debounced at 300ms, searches by title and location name. A clear button (X) appears when text is entered.
- Filter panel (collapsible on web, modal on mobile):
- Year range: two number inputs with en-dash separator (min 1, max 2999)
- Location: text input for neighborhood, district, or city
- Active filter count shown as a badge on the filter button
- Active filter chips: Removable pills showing current filters — search text in quotes (
"hagia sophia"), year range ("1400–1500"), location ("Location: Istanbul"). A "Clear all" button removes all filters. - All filters are URL-driven on web (shareable filtered views) and context-driven on mobile (separate filter states for map and feed).
Story Detail Page
The detail page is an article-style layout (max-w-3xl, centered):
- Back navigation with state-aware routing (returns to map or feed depending on origin)
- Title (large, bold) with delete button for story owners/admins (confirmation dialog)
- Metadata row: location, time period, submission date — each with appropriate icons
- Images in responsive grid (full width for single image, 2-column for multiple)
- Narrative rendered with typographic prose styling (
prose prose-neutral), preserving paragraph breaks - Embedded mini-map: A small Leaflet map (200px height) showing the story's exact location — reinforcing the geographic anchor
- Like button with optimistic updates and comment section with newest-first ordering
On mobile, metadata is displayed as a 2×2 grid of labeled cards, and the mini-map sits in a rounded info-surface section with a floating location label overlay.
Profile and Privacy
User profiles display username, join date, story count, location, total points, and bio. Privacy is a first-class concern:
- Four independent privacy toggles: username, location, birth date, and photo visibility
- On mobile, these are pill-shaped "Public"/"Private" buttons with clear visual distinction (primary-bordered when public)
- Profile editing with dirty-state detection — the save button only appears when changes are detected
The profile avatar on mobile uses the first letter of the username in a colored circle (primary color on info-surface background).
Authentication UX
The login page adapts its messaging to context — when redirected from a protected route like /submit-story, it shows "Log in to submit a story" instead of a generic prompt. Post-login redirects users back to their intended destination.
The registration page includes a real-time password strength indicator: 4 rules (8+ characters, uppercase, lowercase, number) shown as a checklist with green checkmarks or red X icons. Rules only turn red after the first submission attempt to avoid premature error states.
On mobile, authentication uses a single screen with toggle between sign-in and register modes, wrapped in KeyboardAvoidingView for platform-appropriate keyboard behavior. Protected routes show an inline auth form with a contextual info banner above it — no separate redirect needed.
Responsive Navigation
- Web desktop: Sticky top header (56px) with horizontal nav links (Map, Feed, +Submit Story). Active link detection uses exact match for home and prefix match for sub-pages. Auth section shows username with profile link or Login/Register buttons.
- Web mobile: Hamburger menu opens a side drawer (Sheet component) with vertical navigation.
- Mobile app: Custom bottom tab bar with three items — Map (left), a raised floating action button for story submission (58×58 circle, elevated with shadow, centered), and Feed (right). The top bar shows MapPin icon with "StoryMap" branding and contextual back/profile buttons.
Design System
Web: Tailwind CSS with shadcn/ui components. CSS custom properties for theming (bg-background, text-primary, text-destructive, bg-muted). Consistent border radius (rounded-xl for cards, rounded-md for inputs, rounded-full for pills/badges).
Mobile: Custom design tokens — primary near-black (#171717), white background, muted gray (#737373), danger red (#DC2626), success green (#16A34A). Heavy use of pill shapes (borderRadius: 999) for interactive elements and generous card radii (borderRadius: 14-24). Typography uses fontWeight: 800 for headings. Spacing scale: xs=4, sm=8, md=16, lg=24, xl=32.