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:

  1. Title & Narrative — standard text inputs for the story content.
  2. 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.
  3. Place Name — a text field for the human-readable location name (e.g., "Hagia Sophia"), separate from coordinates.
  4. 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.
  5. 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.
  6. Image Upload — file input accepting JPEG/PNG (max 2MB) with inline preview. On mobile, users can choose from gallery or capture with camera.
  7. 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 MapPin icon and time period with Calendar icon
  • 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.