Mobile UX enhancements (desktop unchanged)

- Added mobile-only CSS improvements at end of file
- Enhanced touch targets (min 44px for accessibility)
- Improved carousel with real-time drag feedback and rubber band effect
- Better visual feedback on touch interactions
- Bottom nav active indicator
- Improved stat/chart cards spacing on mobile
- Period banner stacks vertically on mobile
- Table scroll hint with fade gradient
- Extra small screen (≤375px) optimizations
- All changes scoped to @media (max-width: 768px)
This commit is contained in:
fahed
2026-02-03 15:13:34 +03:00
parent 0e5d285680
commit 480885a8e6
5 changed files with 1548 additions and 1653 deletions

View File

@@ -15,10 +15,8 @@ function Carousel({
const [dragOffset, setDragOffset] = useState(0);
const itemCount = React.Children.count(children);
// Threshold for swipe detection
const SWIPE_THRESHOLD = 50;
const VELOCITY_THRESHOLD = 0.3;
const handleTouchStart = useCallback((e) => {
touchStartX.current = e.touches[0].clientX;
touchStartY.current = e.touches[0].clientY;
@@ -32,15 +30,14 @@ function Carousel({
const currentX = e.touches[0].clientX;
const currentY = e.touches[0].clientY;
const diffX = currentX - touchStartX.current;
const diffY = currentY - touchStartY.current;
const diffY = Math.abs(currentY - touchStartY.current);
// Only handle horizontal swipes
if (Math.abs(diffX) > Math.abs(diffY)) {
e.preventDefault();
// Add resistance at edges
// Only handle horizontal swipes (prevent vertical scroll interference)
if (Math.abs(diffX) > diffY) {
// Add resistance at edges (rubber band effect)
let offset = diffX;
if ((activeIndex === 0 && diffX > 0) || (activeIndex === itemCount - 1 && diffX < 0)) {
offset = diffX * 0.3; // Rubber band effect
offset = diffX * 0.25;
}
setDragOffset(offset);
}
@@ -51,10 +48,9 @@ function Carousel({
const endX = e.changedTouches[0].clientX;
const diff = touchStartX.current - endX;
const velocity = Math.abs(diff) / 200; // Rough velocity calc
// Determine if we should change slide
if (Math.abs(diff) > SWIPE_THRESHOLD || velocity > VELOCITY_THRESHOLD) {
// Change slide if swipe exceeds threshold
if (Math.abs(diff) > SWIPE_THRESHOLD) {
if (diff > 0 && activeIndex < itemCount - 1) {
setActiveIndex(activeIndex + 1);
} else if (diff < 0 && activeIndex > 0) {
@@ -77,9 +73,11 @@ function Carousel({
}
}, [activeIndex, setActiveIndex, itemCount]);
// Calculate transform
// Calculate transform with drag offset
const baseTransform = -(activeIndex * 100);
const dragPercentage = trackRef.current ? (dragOffset / trackRef.current.offsetWidth) * 100 : 0;
const dragPercentage = trackRef.current
? (dragOffset / trackRef.current.offsetWidth) * 100
: 0;
const transform = baseTransform + dragPercentage;
return (
@@ -97,7 +95,7 @@ function Carousel({
className="carousel-track"
style={{
transform: `translateX(${transform}%)`,
transition: isDragging ? 'none' : 'transform 400ms cubic-bezier(0.25, 0.46, 0.45, 0.94)'
transition: isDragging ? 'none' : undefined
}}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
@@ -109,7 +107,6 @@ function Carousel({
key={i}
role="tabpanel"
aria-hidden={activeIndex !== i}
aria-label={labels[i] || `Slide ${i + 1}`}
>
{child}
</div>
@@ -127,7 +124,6 @@ function Carousel({
role="tab"
aria-selected={activeIndex === i}
aria-label={labels[i] || `Slide ${i + 1}`}
aria-controls={`slide-${i}`}
>
{showLabels && labels[i] && (
<span className="dot-label">{labels[i]}</span>