Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- iOS navigation bar redesigned as a floating pill — rounded-full shape, frosted-glass background (`rgba(255,255,255,0.80)`), drop shadow, and `mb-2 mx-2` margins replacing the full-width border-top bar
— `src/components/MobileNav.tsx`
- Page title heading hidden on non-iOS web builds (`hidden` class added to title `div` in `PageLayout`)
— `src/components/PageLayout.tsx`
- Homepage stats grid changed from single-column to two-column on mobile (`grid-cols-2`)
— `src/pages/Index.tsx`
- Todo list item alignment in `TaskTrackingPanel` changed from `items-start` to `items-center`
— `src/components/TaskTrackingPanel.tsx`
- Theme accent color changed from `orange` to `blue`
— `src/main.tsx`

### Added

- Apple HIG compliance pass for the native iOS app
Expand Down
2 changes: 1 addition & 1 deletion dev-dist/sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ define(['./workbox-21a80088'], (function (workbox) { 'use strict';
*/
workbox.precacheAndRoute([{
"url": "index.html",
"revision": "0.utnvbc1mnuk"
"revision": "0.u5tkfl26p2g"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
Expand Down
12 changes: 10 additions & 2 deletions ios/App/CapApp-SPM/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@ let package = Package(
targets: ["CapApp-SPM"])
],
dependencies: [
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "8.3.1")
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "8.3.1"),
.package(name: "CapacitorApp", path: "../../../node_modules/@capacitor/app"),
.package(name: "CapacitorHaptics", path: "../../../node_modules/@capacitor/haptics"),
.package(name: "CapacitorKeyboard", path: "../../../node_modules/@capacitor/keyboard"),
.package(name: "CapacitorStatusBar", path: "../../../node_modules/@capacitor/status-bar")
],
targets: [
.target(
name: "CapApp-SPM",
dependencies: [
.product(name: "Capacitor", package: "capacitor-swift-pm"),
.product(name: "Cordova", package: "capacitor-swift-pm")
.product(name: "Cordova", package: "capacitor-swift-pm"),
.product(name: "CapacitorApp", package: "CapacitorApp"),
.product(name: "CapacitorHaptics", package: "CapacitorHaptics"),
.product(name: "CapacitorKeyboard", package: "CapacitorKeyboard"),
.product(name: "CapacitorStatusBar", package: "CapacitorStatusBar")
]
)
]
Expand Down
35 changes: 22 additions & 13 deletions public/pwa.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ html {
/* Prevent text selection on buttons in PWA */
button,
.button,
[role="button"] {
[role='button'] {
-webkit-user-select: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
Expand All @@ -57,7 +57,6 @@ button,

/* Landscape mode optimizations */
@media (orientation: landscape) and (max-height: 500px) {

/* Compact header for landscape mobile */
header {
height: 48px;
Expand All @@ -72,10 +71,9 @@ button,

/* Standalone mode specific styles (when app is installed) */
@media (display-mode: standalone) {

/* Add subtle indication that app is installed */
body::before {
content: "";
content: '';
position: fixed;
top: 0;
left: 0;
Expand Down Expand Up @@ -106,7 +104,21 @@ body {

/* Improve button touch targets on mobile */
@media (max-width: 768px) {
button:not([aria-hidden="true"]) {
button:not([aria-hidden='true']) {
min-height: 44px;
min-width: 44px;
}
button[role='checkbox'] {
min-height: 24px;
min-width: 24px;
}
}

/* Improve input targets on mobile */
@media (max-width: 768px) {
input:not([aria-hidden='true']),
textarea:not([aria-hidden='true']),
select:not([aria-hidden='true']) {
min-height: 44px;
min-width: 44px;
}
Expand All @@ -118,7 +130,6 @@ body {
}

@keyframes pulse {

0%,
100% {
opacity: 1;
Expand Down Expand Up @@ -156,7 +167,6 @@ body {

/* iOS specific fixes */
@supports (-webkit-touch-callout: none) {

/* Fix iOS safari bounce */
body {
position: fixed;
Expand All @@ -172,10 +182,10 @@ body {
}

/* Fix iOS input zoom */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type='text'],
input[type='email'],
input[type='password'],
input[type='number'],
select,
textarea {
font-size: 16px !important;
Expand Down Expand Up @@ -247,10 +257,9 @@ body {

/* Android specific fixes */
@media (max-width: 768px) {

/* Prevent address bar from covering content */
.mobile-viewport {
min-height: 100vh;
min-height: -webkit-fill-available;
}
}
}
156 changes: 85 additions & 71 deletions src/components/MobileNav.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,92 @@
import { memo } from "react";
import { Link, useLocation } from "react-router-dom";
import { Home, Archive, Settings, PaperclipIcon, ClipboardList } from "lucide-react";
import { useAuth } from "@/hooks/useAuth";
import { useHaptics } from "@/hooks/useHaptics";
import { memo } from 'react';
import { Link, useLocation } from 'react-router-dom';
import {
Home,
Archive,
Settings,
PaperclipIcon,
ClipboardList
} from 'lucide-react';
import { useAuth } from '@/hooks/useAuth';
import { useHaptics } from '@/hooks/useHaptics';

export const MobileNav = memo(function MobileNav() {
const location = useLocation();
const { isAuthenticated } = useAuth();
const { lightImpact } = useHaptics();
const location = useLocation();
const { isAuthenticated } = useAuth();
const { lightImpact } = useHaptics();

const isActive = (path: string) => {
return location.pathname === path;
};
const isActive = (path: string) => {
return location.pathname === path;
};

const navItems = [
{
path: "/",
icon: Home,
label: "Home"
},
{
path: "/tasks",
icon: ClipboardList,
label: "Tasks"
},
...(isAuthenticated ? [{
path: "/report",
icon: PaperclipIcon,
label: "Report"
}] : []),
{
path: "/archive",
icon: Archive,
label: "Archive"
},
{
path: "/settings",
icon: Settings,
label: "Settings"
}
];
const navItems = [
{
path: '/',
icon: Home,
label: 'Home'
},
{
path: '/tasks',
icon: ClipboardList,
label: 'Tasks'
},
...(isAuthenticated
? [
{
path: '/report',
icon: PaperclipIcon,
label: 'Report'
}
]
: []),
{
path: '/archive',
icon: Archive,
label: 'Archive'
},
{
path: '/settings',
icon: Settings,
label: 'Settings'
}
];

const gridClass =
navItems.length <= 3
? "grid-cols-3"
: navItems.length === 4
? "grid-cols-4"
: "grid-cols-5";
const gridClass =
navItems.length <= 3
? 'grid-cols-3'
: navItems.length === 4
? 'grid-cols-4'
: 'grid-cols-5';

return (
<nav
className="fixed bottom-0 left-0 right-0 bg-background border-t border-border md:hidden z-40 mobile-nav-ios print:hidden"
style={{
paddingBottom: "max(env(safe-area-inset-bottom, 0px), 0px)"
}}
>
<div className={`grid ${gridClass} h-12`}>
{navItems.map(({ path, icon: Icon, label }) => (
<Link
key={path}
to={path}
onClick={lightImpact}
className={`flex flex-col items-center justify-center space-y-1 transition-colors touch-manipulation ${isActive(path)
? "text-primary"
: "text-muted-foreground hover:text-foreground"
}`}
aria-label={label}
aria-current={isActive(path) ? "page" : undefined}
>
<Icon className="h-5 w-5" />
<span className="text-xs font-medium">{label}</span>
</Link>
))}
</div>
</nav>
);
return (
<nav
className="fixed bottom-0 left-0 right-0 mb-2 mx-2 rounded-full border border-gray-200 md:hidden z-40 mobile-nav-ios print:hidden"
style={{
padding: 'max(env(safe-area-inset-bottom, 8px), 8px)',
boxShadow:
'0 4px 16px -8px rgba(0,0,0,0.10), 0 3px 12px -4px rgba(0,0,0,0.10), 0 2px 3px -2px rgba(0, 78, 194, 0.08)',
background: 'rgba(255,255,255,0.80)'
}}
>
<div className={`grid ${gridClass} h-12`}>
{navItems.map(({ path, icon: Icon, label }) => (
<Link
key={path}
to={path}
onClick={lightImpact}
className={`flex flex-col items-center justify-center space-y-1 transition-colors touch-manipulation ${
isActive(path)
? 'text-primary'
: 'text-muted-foreground hover:text-foreground'
}`}
aria-label={label}
aria-current={isActive(path) ? 'page' : undefined}
>
<Icon className="h-5 w-5" />
<span className="text-xs font-medium">{label}</span>
</Link>
))}
</div>
</nav>
);
});
2 changes: 1 addition & 1 deletion src/components/PageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const PageLayout = ({
<SiteNavigationMenu />
)}
{!isIosBuild && title !== undefined && (
<div className="max-w-6xl mx-auto pt-4 pb-2 px-4 md:p-6 print:p-4">
<div className="hidden max-w-6xl mx-auto pt-4 pb-2 px-4 md:p-6 print:p-4">
<div className="flex items-center justify-between">
<h1 className="md:text-2xl font-bold text-foreground flex items-center gap-2">
{icon}
Expand Down
2 changes: 1 addition & 1 deletion src/components/TaskTrackingPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function TaskTrackingPanel() {
{activeTodos.length > 0 && (
<ul className="space-y-2">
{activeTodos.map((item) => (
<li key={item.id} className="flex items-start gap-2 group">
<li key={item.id} className="flex items-center gap-2 group">
<Checkbox
id={`todo-${item.id}`}
checked={false}
Expand Down
2 changes: 1 addition & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ if (import.meta.env.VITE_IOS_BUILD === "true") {
}

createRoot(document.getElementById('root')!).render(
<Theme accentColor="orange" grayColor="slate" radius="full">
<Theme accentColor="blue" grayColor="slate" radius="full">
<App />
{/* <ThemePanel /> */}
</Theme>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const TimeTrackerContent = () => {

{/* Stats (always visible) */}
{!isDayStarted && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 print:hidden">
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 print:hidden">
<Card>
<CardContent className="p-4">
<div className="text-2xl font-bold text-primary">
Expand Down
Loading