Skip to content

Feat/responsive - #1060

Draft
jona159 wants to merge 19 commits into
devfrom
feat/responsive
Draft

Feat/responsive#1060
jona159 wants to merge 19 commits into
devfrom
feat/responsive

Conversation

@jona159

@jona159 jona159 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Type of Change

  • Dependency upgrade
  • Bug fix (non-breaking change)
  • Breaking change
    • e.g. a fixed bug or new feature that may break something else
  • New feature
  • Code quality improvements
    • e.g. refactoring, documentation, tests, tooling, ...

Implementation

Checklist

  • I gave this pull request a meaningful title
  • My pull request is targeting the dev branch
  • I have added documentation to my code
  • I have deleted code that I have commented out

Additional Information

  • This PR closes #

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f6928b6e-2c50-4134-9e77-a104f3a92bf1

📥 Commits

Reviewing files that changed from the base of the PR and between ec7891c and ae1472f.

📒 Files selected for processing (61)
  • app/components/device-detail/device-detail-box.tsx
  • app/components/device-detail/graph.tsx
  • app/components/device/new/custom-device-config.tsx
  • app/components/device/new/general-info.tsx
  • app/components/device/new/location-info.tsx
  • app/components/device/new/new-device-stepper.tsx
  • app/components/error-message.tsx
  • app/components/header/download.tsx
  • app/components/header/home/index.tsx
  • app/components/header/info/index.tsx
  • app/components/header/menu/index.tsx
  • app/components/header/nav-bar/index.tsx
  • app/components/landing/footer.tsx
  • app/components/landing/header/header.tsx
  • app/components/landing/header/language-selector.tsx
  • app/components/landing/header/theme-toggle.tsx
  • app/components/landing/sections/connect.tsx
  • app/components/landing/sections/features.tsx
  • app/components/landing/sections/integrations.tsx
  • app/components/landing/sections/partners.tsx
  • app/components/landing/sections/pricing-plans.tsx
  • app/components/landing/stats.tsx
  • app/components/language-select.tsx
  • app/components/map/legend.tsx
  • app/components/map/topbar.tsx
  • app/components/markdown-content.tsx
  • app/components/mydevices/dt/data-table.tsx
  • app/components/nav-bar.tsx
  • app/components/search/search-list-item.tsx
  • app/components/search/search-list.tsx
  • app/components/theme-select.tsx
  • app/components/ui/alert-dialog.tsx
  • app/components/ui/dialog.tsx
  • app/components/ui/drawer.tsx
  • app/components/ui/sheet.tsx
  • app/components/ui/table.tsx
  • app/components/ui/tabs.tsx
  • app/components/ui/toast.tsx
  • app/components/viewport-portal.tsx
  • app/root.tsx
  • app/routes/about.tsx
  • app/routes/account.password-reset.tsx
  • app/routes/device.$deviceId.dataupload.tsx
  • app/routes/device.$deviceId.edit.security.tsx
  • app/routes/device.$deviceId.edit.sensors.tsx
  • app/routes/device.$deviceId.edit.transfer.tsx
  • app/routes/device.$deviceId.edit.tsx
  • app/routes/device.dashboard.$deviceId.tsx
  • app/routes/device.new.tsx
  • app/routes/device.transfer.tsx
  • app/routes/docs.tsx
  • app/routes/explore.forgot.tsx
  • app/routes/explore.login.tsx
  • app/routes/explore.register.tsx
  • app/routes/imprint.tsx
  • app/routes/privacy.tsx
  • app/routes/profile.$username.tsx
  • app/routes/settings.account.tsx
  • app/routes/settings.preferences.tsx
  • app/routes/settings.profile.tsx
  • app/routes/settings.tsx
 ____________________________________________
< Like a moth to a flame, I'm drawn to bugs. >
 --------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/responsive

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 65.97% 2242 / 3398
🔵 Statements 64.51% 2316 / 3590
🔵 Functions 63.21% 433 / 685
🔵 Branches 50.79% 1080 / 2126
File CoverageNo changed files found.
Generated in workflow #2862 for commit ae1472f by the Vitest Coverage Report Action

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f6928b6e-2c50-4134-9e77-a104f3a92bf1

📥 Commits

Reviewing files that changed from the base of the PR and between ec7891c and ae1472f.

📒 Files selected for processing (61)
  • app/components/device-detail/device-detail-box.tsx
  • app/components/device-detail/graph.tsx
  • app/components/device/new/custom-device-config.tsx
  • app/components/device/new/general-info.tsx
  • app/components/device/new/location-info.tsx
  • app/components/device/new/new-device-stepper.tsx
  • app/components/error-message.tsx
  • app/components/header/download.tsx
  • app/components/header/home/index.tsx
  • app/components/header/info/index.tsx
  • app/components/header/menu/index.tsx
  • app/components/header/nav-bar/index.tsx
  • app/components/landing/footer.tsx
  • app/components/landing/header/header.tsx
  • app/components/landing/header/language-selector.tsx
  • app/components/landing/header/theme-toggle.tsx
  • app/components/landing/sections/connect.tsx
  • app/components/landing/sections/features.tsx
  • app/components/landing/sections/integrations.tsx
  • app/components/landing/sections/partners.tsx
  • app/components/landing/sections/pricing-plans.tsx
  • app/components/landing/stats.tsx
  • app/components/language-select.tsx
  • app/components/map/legend.tsx
  • app/components/map/topbar.tsx
  • app/components/markdown-content.tsx
  • app/components/mydevices/dt/data-table.tsx
  • app/components/nav-bar.tsx
  • app/components/search/search-list-item.tsx
  • app/components/search/search-list.tsx
  • app/components/theme-select.tsx
  • app/components/ui/alert-dialog.tsx
  • app/components/ui/dialog.tsx
  • app/components/ui/drawer.tsx
  • app/components/ui/sheet.tsx
  • app/components/ui/table.tsx
  • app/components/ui/tabs.tsx
  • app/components/ui/toast.tsx
  • app/components/viewport-portal.tsx
  • app/root.tsx
  • app/routes/about.tsx
  • app/routes/account.password-reset.tsx
  • app/routes/device.$deviceId.dataupload.tsx
  • app/routes/device.$deviceId.edit.security.tsx
  • app/routes/device.$deviceId.edit.sensors.tsx
  • app/routes/device.$deviceId.edit.transfer.tsx
  • app/routes/device.$deviceId.edit.tsx
  • app/routes/device.dashboard.$deviceId.tsx
  • app/routes/device.new.tsx
  • app/routes/device.transfer.tsx
  • app/routes/docs.tsx
  • app/routes/explore.forgot.tsx
  • app/routes/explore.login.tsx
  • app/routes/explore.register.tsx
  • app/routes/imprint.tsx
  • app/routes/privacy.tsx
  • app/routes/profile.$username.tsx
  • app/routes/settings.account.tsx
  • app/routes/settings.preferences.tsx
  • app/routes/settings.profile.tsx
  • app/routes/settings.tsx

Comment on lines +91 to +93
className="inline-flex size-11 items-center justify-center rounded-lg p-2 text-sm text-gray-500 hover:bg-gray-100 focus:ring-2 focus:ring-gray-200 focus:outline-hidden lg:hidden"
aria-controls="navbar-cta"
aria-expanded="false"
aria-expanded={openMenu}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Point aria-controls at the rendered mobile menu.

aria-expanded={openMenu} now follows the state, but aria-controls="navbar-cta" still names the desktop navigation element. That element is hidden below lg. The visible mobile menu is the separate element rendered at Line 110 and has no matching id. Assistive technology receives a false control relationship.

Give the mobile menu its own id, use that id in aria-controls, reference the actual trigger from aria-labelledby, and update the hidden label at Line 95 when openMenu is true.

Proposed ARIA fix
 <button
+	id="navbar-menu-toggle"
 	...
-	data-collapse-toggle="navbar-cta"
+	data-collapse-toggle="navbar-mobile-menu"
 	...
-	aria-controls="navbar-cta"
+	aria-controls="navbar-mobile-menu"
 	aria-expanded={openMenu}
 >
-	<span className="sr-only">Open main menu</span>
+	<span className="sr-only">
+		{openMenu ? 'Close main menu' : 'Open main menu'}
+	</span>
 ...
 <div
+	id="navbar-mobile-menu"
 	...
-	aria-labelledby="options-menu"
+	aria-labelledby="navbar-menu-toggle"
 >

<div className="mt-4 flex flex-col justify-center gap-3 sm:flex-row sm:gap-5">
<div id="left" className="flex w-full flex-col gap-3 sm:w-auto">
<a
href="http://github.com/openSenseMap/frontend"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Use an HTTPS repository URL.

The changed href uses http://github.com/openSenseMap/frontend. Use the HTTPS URL directly instead of relying on redirects or HSTS behavior.

Proposed fix
-							href="http://github.com/openSenseMap/frontend"
+							href="https://github.com/openSenseMap/frontend"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
href="http://github.com/openSenseMap/frontend"
href="https://github.com/openSenseMap/frontend"

Comment on lines 50 to +51
size="topbarPill"
className="absolute top-14 left-0 h-11 px-3 lg:static lg:h-10"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the shared topbar variant on large screens.

When lg:static returns the “My Area” button to the desktop header row, omitting variant="topbar" selects the Button default instead of the shared topbar surface. The Button implementation defines these as different variants, while the adjacent controls keep variant="topbar". (raw.githubusercontent.com)

Keep the topbar variant. Use the responsive classes only for placement and size. If a different mobile appearance is required, override it explicitly below lg.

Proposed fix
 									<Button
 										type="button"
+										variant="topbar"
 										size="topbarPill"
 										className="absolute top-14 left-0 h-11 px-3 lg:static lg:h-10"

Source: MCP tools


const searchListItemStyle = cva(
'data-[active=true]:bg-light-green relative my-1 flex h-8 items-center gap-2 rounded-lg px-2 data-[active=true]:text-white',
'data-[active=true]:bg-light-green relative my-1 flex min-h-11 cursor-pointer items-center gap-2 rounded-lg px-2 data-[active=true]:text-white md:h-8 md:min-h-0',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep mobile result targets until the desktop breakpoint.

Line 20 reduces result rows to 32px at md. The search panel remains in its mobile layout until lg. Touch users between 768px and 1023px therefore get smaller result targets. Use lg for the compact row height.

Proposed fix
- '... min-h-11 ... md:h-8 md:min-h-0'
+ '... min-h-11 ... lg:h-8 lg:min-h-0'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'data-[active=true]:bg-light-green relative my-1 flex min-h-11 cursor-pointer items-center gap-2 rounded-lg px-2 data-[active=true]:text-white md:h-8 md:min-h-0',
'data-[active=true]:bg-light-green relative my-1 flex min-h-11 cursor-pointer items-center gap-2 rounded-lg px-2 data-[active=true]:text-white lg:h-8 lg:min-h-0',

Comment thread app/routes/about.tsx
const { t } = useTranslation('landing')

const isDesktop = useMediaQuery('(min-width: 768px)')
const showGlobe = useMediaQuery('(min-width: 1120px)')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C4 'useMediaQuery|showGlobe|GlobeComponent' app/routes/about.tsx
fd -H -t f -E node_modules -E .git . -x rg -n -C2 'ssr|serverBuild|prerender' {} || true

Repository: openSenseMap/frontend

Length of output: 1421


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- about route ---'
sed -n '80,220p' app/routes/about.tsx

printf '%s\n' '--- globe component ---'
globe_file="$(fd -H -t f -E node_modules -E .git -i 'globe.client*' . | head -n 1)"
if [ -n "$globe_file" ]; then
  sed -n '1,240p' "$globe_file"
else
  printf '%s\n' 'globe.client file not found'
fi

printf '%s\n' '--- SSR configuration candidates ---'
fd -H -t f -E node_modules -E .git . \
  | while IFS= read -r file; do
      case "$file" in
        *config*|*package.json|*vite*|*entry*|*root*|*route*)
          if rg -n -q 'ssr|serverBuild|prerender|hydrate|useMediaQuery|GlobeComponent' "$file" 2>/dev/null; then
            printf '%s\n' "### $file"
            rg -n -C2 'ssr|serverBuild|prerender|hydrate|useMediaQuery|GlobeComponent' "$file"
          fi
          ;;
      esac
    done

printf '%s\n' '--- Mantine dependency versions ---'
for file in package.json package-lock.json pnpm-lock.yaml yarn.lock bun.lockb; do
  if [ -f "$file" ]; then
    printf '%s\n' "### $file"
    rg -n -C2 '`@mantine/hooks`|mantine' "$file" | head -80
  fi
done

Repository: openSenseMap/frontend

Length of output: 8850


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from urllib.request import urlopen
import re

url = "https://unpkg.com/@mantine/hooks@9.4.2/lib/use-media-query/use-media-query.js"
source = urlopen(url, timeout=10).read().decode()
print(source)
print("\n--- relevant behavior ---")
for pattern in (
    r"useState\([^;]+",
    r"useEffect\([^;]+",
    r"matchMedia",
    r"getInitialValueInEffect",
):
    print(f"{pattern}: {bool(re.search(pattern, source))}")
PY

Repository: openSenseMap/frontend

Length of output: 2445


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

curl -fsSLk 'https://unpkg.com/@mantine/hooks@9.4.2/lib/use-media-query/use-media-query.js' \
  | sed -n '1,220p'

Repository: openSenseMap/frontend

Length of output: 261


🌐 Web query:

Mantine @mantine/hooks 9.4.2 useMediaQuery source getInitialValueInEffect SSR

💡 Result:

In Mantine's @mantine/hooks library, getInitialValueInEffect is a configuration option available in the useMediaQuery hook that dictates how the hook determines its initial return value when the application is rendered [1][2]. By default, getInitialValueInEffect is set to true [3]. When enabled (true), the hook will return the initial value provided as the second argument (or false if none is provided) during the initial render [1][3]. It then updates to the actual media query result inside a useEffect hook after the component has mounted on the client [3]. This behavior is designed to prevent hydration mismatches in server-side rendering (SSR) environments, where the server cannot access the window.matchMedia API [1][4]. If you set getInitialValueInEffect to false, the hook will attempt to evaluate the media query immediately during the initial render [1][3]. If window.matchMedia is available (i.e., on the client side), it will return the real result immediately [3]. However, if used in an SSR environment, this can lead to hydration mismatches because the server-rendered HTML will not match the initial state calculated by the client [1][5]. For most SSR applications (like Next.js), it is recommended to keep getInitialValueInEffect as true (the default) if the hook's output is used in the rendered markup to ensure consistency between the server-rendered HTML and the initial client-side hydration [1][4][6]. If your application does not use SSR, or if the hook's value is not used in the initial rendered output (e.g., used only for event listeners or non-SSR components), you may safely set it to false to get an immediate value [1][5][7]. top_results: [1][3][4][2][7][6]

Citations:


Reserve the globe space during SSR.

On desktop, useMediaQuery('(min-width: 1120px)') adds GlobeComponent after hydration. This changes the hero layout and causes layout shift. Render a stable 500px container and control visibility with CSS, or reserve the space during client-only mounting.

Comment on lines +314 to +331
<Input
id="username"
placeholder={t('enter_username')}
ref={usernameRef}
name="username"
type="text"
autoFocus={true}
/>
<p className="text-muted-foreground text-xs">
{t('username_hint')}
</p>
{actionErrors?.username && (
<div
className="mt-1 text-sm text-red-500"
id="password-error"
>
{t(actionErrors?.username)}
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Associate the username error with the username input.

Line 328 assigns password-error to the username error. The username Input does not set aria-invalid or aria-describedby. Screen readers cannot identify the validation error for the focused username field.

Proposed fix
 <Input
   id="username"
+  aria-invalid={actionErrors?.username ? true : undefined}
+  aria-describedby="username-error"
   placeholder={t('enter_username')}
   ref={usernameRef}
   name="username"
   type="text"
   autoFocus={true}
 />
 ...
-<div className="mt-1 text-sm text-red-500" id="password-error">
+<div className="mt-1 text-sm text-red-500" id="username-error">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Input
id="username"
placeholder={t('enter_username')}
ref={usernameRef}
name="username"
type="text"
autoFocus={true}
/>
<p className="text-muted-foreground text-xs">
{t('username_hint')}
</p>
{actionErrors?.username && (
<div
className="mt-1 text-sm text-red-500"
id="password-error"
>
{t(actionErrors?.username)}
</div>
<Input
id="username"
aria-invalid={actionErrors?.username ? true : undefined}
aria-describedby="username-error"
placeholder={t('enter_username')}
ref={usernameRef}
name="username"
type="text"
autoFocus={true}
/>
<p className="text-muted-foreground text-xs">
{t('username_hint')}
</p>
{actionErrors?.username && (
<div
className="mt-1 text-sm text-red-500"
id="username-error"
>
{t(actionErrors?.username)}
</div>

Comment on lines +640 to +641
<div className="flex flex-col items-start justify-between gap-3 py-4 last:pb-0 sm:flex-row sm:items-center sm:gap-4">
<div className="flex min-w-0 items-start gap-2">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- changed files ---'
git diff --stat

printf '%s\n' '--- target file ---'
sed -n '620,690p' app/routes/settings.preferences.tsx

printf '%s\n' '--- related checkbox and layout code ---'
sed -n '500,575p' app/routes/settings.preferences.tsx
rg -n -C 5 'newsletter|checkbox|pending|items-start|min-w-0|w-full|sm:w-auto' app/routes/settings.preferences.tsx app/components/language-select.tsx

Repository: openSenseMap/frontend

Length of output: 24843


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- component definitions ---'
rg -n -C 8 'export (function|const) Label|forwardRef|function Label|export (function|const) CardContent' app/components/ui/label.tsx app/components/ui/card.tsx

printf '%s\n' '--- utility and responsive configuration ---'
rg -n -C 3 'tailwind|tailwindcss|container|screens|theme' package.json tailwind.config.* app/styles* 2>/dev/null || true

printf '%s\n' '--- newsletter translations ---'
rg -n -C 3 'receive_newsletter_messages|newsletter_confirmation_pending' . --glob '!node_modules' --glob '!build' --glob '!dist'

printf '%s\n' '--- relevant source and git state ---'
git status --short
git diff -- app/routes/settings.preferences.tsx

Repository: openSenseMap/frontend

Length of output: 10582


Keep the newsletter controls within the mobile width.

At Line 640, items-start prevents the checkbox group from stretching in the column layout. Add w-full sm:w-auto to the group so long localized labels wrap within the card.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant