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
1 change: 1 addition & 0 deletions _plans/adjust-for-mobile.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adjust the contents of the app so it is responsive and will display well on phones and tablets. Ensure the video is always visible and hide the other controls as necessary. Shrink controls where possible but leave the video playback as the main feature.
48 changes: 48 additions & 0 deletions _specs/mobile-responsive-layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Spec for mobile-responsive-layout

branch: claude/feature/mobile-responsive-layout

## Summary
Make the entire app responsive so it displays well on phones and tablets. The video playback is the primary feature and must always be visible. Secondary controls (navigation, term list, shuffle, etc.) should be shrunk or hidden as needed to prioritize the video on smaller screens.

## Functional Requirements
- The flashcard session screen must display the video at a usable size on all screen sizes down to 375px wide (iPhone SE)
- The term selector list should be hidden on mobile and accessible via a collapsible/toggle control
- Navigation buttons (Prev, Next, Shuffle) should be visible but compact on mobile
- The position indicator (e.g. "3 / 45") should remain visible on mobile
- The Back button should remain accessible on all screen sizes
- The session title and description should be visible but may be reduced in font size on mobile
- The category selector (TermInput) layout should stack to a single column on mobile, with the resources panel below the categories
- The ASL resources panel can be collapsed or hidden on mobile to save space
- On tablets (768px–1024px), a two-column layout may be preserved where it fits; fall back gracefully where it does not
- Touch targets (buttons) must be at least 44×44px on mobile per accessibility guidelines

## Possible Edge Cases
- Very long term names may overflow the flashcard card on narrow screens
- The video iframe aspect ratio must be preserved and never clipped on any viewport
- The term selector list, when toggled open on mobile, should not push the video off screen
- Category button grid may have an odd number of items — the last cell must not stretch awkwardly

## Acceptance Criteria
- Loading the app on a 375px-wide viewport shows the flashcard video without horizontal scroll
- The video occupies the majority of the vertical space on mobile in the session view
- The term list is hidden by default on mobile and can be revealed via a clearly labeled toggle
- All interactive controls remain reachable and meet 44px minimum touch target size
- No horizontal overflow at 375px, 768px, or 1024px viewport widths
- The category selector page is usable on mobile with no overlapping or clipped elements
- Tablet layout (768px) makes sensible use of the available horizontal space

## Open Questions
- Should the term list toggle be a drawer/overlay or an inline expand on mobile?
- drawer/overlay
- Should the session title/description be hidden entirely on mobile to maximize video space, or just reduced?
- hidden entirely
- Is there a preferred breakpoint for "tablet" behavior, or should we use 640px and 1024px as the two breakpoints?
- use 640px and 1024px as the two breakpoints.

## Testing Guidelines
Create a test file(s) in the ./tests folder for the new feature, and create meaningful tests for the following cases, without going too heavy:
- TermInput renders in single-column layout when viewport width is below the mobile breakpoint
- FlashcardSession renders the video element at all viewport sizes
- Term selector toggle button is present in the DOM on mobile viewports
- No critical UI elements are hidden on mobile that should remain accessible (Back button, nav controls)
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
"test:ui": "vitest --ui"
},
"dependencies": {
"@tippyjs/react": "^4.2.6",
"axios": "^1.15.0",
"react": "^19.2.4",
"react-dom": "^19.2.4"
"react-dom": "^19.2.4",
"tippy.js": "^6.3.7"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
Expand Down
207 changes: 197 additions & 10 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@

.term-input__body {
display: grid;
grid-template-columns: 220px 1fr;
grid-template-columns: 1fr 1fr;
gap: 40px;
align-items: start;
flex-grow: 1;
Expand All @@ -69,9 +69,50 @@
/* ── Category buttons ─────────────────────────────────── */

.term-input__categories {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;

@media (max-width: 640px) {
grid-template-columns: 1fr;
}
}

/* ── Resources panel ──────────────────────────────────── */

.term-input__resources {
padding: 20px 24px;
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: 12px;
}

.term-input__resources-heading {
margin: 0 0 14px;
font-size: 15px;
font-weight: 700;
color: var(--text-h);
letter-spacing: 0.02em;
}

.term-input__resources-list {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 12px;
gap: 10px;

a {
color: var(--accent);
text-decoration: none;
font-size: 14px;
line-height: 1.4;

&:hover {
text-decoration: underline;
}
}
}

.btn-category {
Expand All @@ -85,8 +126,17 @@
font-weight: 600;
text-align: left;
cursor: pointer;
display: flex;
align-items: center;
gap: 10px;
transition: border-color 0.2s, box-shadow 0.2s, background-color 0.2s;

.btn-category__icon {
font-size: 20px;
line-height: 1;
flex-shrink: 0;
}

&:hover {
background: var(--accent-bg);
border-color: var(--accent-border);
Expand Down Expand Up @@ -191,8 +241,11 @@

.flashcard-session-header {
display: flex;
align-items: flex-start;
gap: 16px;
flex-direction: column;
gap: 4px;
text-align: center;
width: 100%;
max-width: 560px;
}

.flashcard-session-title {
Expand Down Expand Up @@ -258,8 +311,8 @@
align-items: center;
justify-content: center;
width: 100%;
max-width: 560px;
border-radius: 16px;
padding: 24px 32px;
box-sizing: border-box;
text-align: center;
transition: background-color 0.25s ease;
Expand All @@ -271,7 +324,7 @@
letter-spacing: -0.02em;
overflow-wrap: break-word;
word-break: break-word;
line-height: 1.6;
line-height: 1;
/*overflow: hidden;*/
max-height: 100%;
}
Expand Down Expand Up @@ -397,21 +450,155 @@
}
}

/* ── Term select ──────────────────────────────────────── */
/* ── Term drawer (sidebar on desktop, overlay on mobile) ── */

.term-select {
.term-drawer {
flex: 0 0 200px;
align-self: stretch;
display: flex;
flex-direction: column;
overflow: hidden;
border: 1px solid var(--border);
border-radius: 8px;

@media (max-width: 1024px) {
flex: 0 0 160px;
}

@media (max-width: 640px) {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 60vh;
z-index: 200;
display: none;
flex-direction: column;
background: var(--bg);
border: none;
border-top: 1px solid var(--border);
border-radius: 16px 16px 0 0;
}
}

.term-drawer--open {
@media (max-width: 640px) {
display: flex;
}
}

.term-drawer__backdrop {
display: none;

@media (max-width: 640px) {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 199;

.term-drawer--open & {
display: block;
}
}
}

.term-drawer__panel {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}

.term-drawer__header {
display: none;

@media (max-width: 640px) {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
font-size: 15px;
font-weight: 600;
color: var(--text-h);
}
}

.term-drawer__close {
background: none;
border: none;
font-size: 18px;
cursor: pointer;
color: var(--text);
padding: 4px 8px;
line-height: 1;

&:hover {
color: var(--text-h);
}
}

/* ── Term select ──────────────────────────────────────── */

.term-select {
flex-grow: 1;
overflow-y: auto;
background: var(--code-bg);
color: var(--text);
border: 1px solid var(--border);
border-radius: 8px;
border: none;
border-radius: 0;
font-size: 15px;
padding: 4px;
cursor: pointer;
width: 100%;

option {
padding: 4px 8px;
}
}

/* ── Mobile responsive overrides ─────────────────────── */

@media (max-width: 640px) {
.flashcard-session-body {
flex-direction: column;
gap: 8px;
}

.flashcard-session-header {
display: none;
}

.flashcard-video {
max-width: 100%;
}

.flashcard-card {
padding: 12px 16px;
min-height: 40px;
}

.flashcard-term {
font-size: clamp(18px, 5vw, 48px);
}

.btn-nav {
padding: 8px 14px;
}

.btn-nav--terms {
display: inline-flex;
}
}

.btn-nav--terms {
display: none;
}

@media (max-width: 1024px) {
.flashcard-video {
max-width: 100%;
}
}
Loading
Loading