Mobile/UX overhaul: collapsible cards, topic filter, progress & resume, a11y - #11
Merged
Conversation
On phones the topbar wraps the search to its own row, leaving the brand,
Star, and theme-toggle buttons packed on the left with empty space on the
right. Add .brand{margin-right:auto} at <=640px so the brand keeps the left
edge and the Star + theme buttons sit at the right edge — the conventional,
balanced app-header layout. Desktop is unaffected (its margin-left:auto on
.search already handles spacing).
- Track cards: tighten tile padding, lesson-chip size/spacing, and tfoot gap at <=640px so cards don't take the whole screen on mobile. - Footer: center the columns (brand block, Quick links, Connect/social) on mobile — the social links (Medium, LinkedIn, etc.) now sit centered. CSS-only; desktop unaffected. Same branch as the header right-align fix.
Wrap each track card's lesson list in a native <details>. On mobile the list collapses behind a 'Lessons' summary so cards are short and several tracks fit on screen at once; tap to expand. On desktop the lessons stay visible as before (a small script syncs the details 'open' state to the viewport via matchMedia, placed right after the grid so desktop expands during parse with no flash and mobile stays collapsed). Lesson <a> links remain in the DOM for SEO/crawlers. Markup change is uniform across all 9 tiles; CSS + script only, desktop view unchanged.
…tion states - Cards collapse on every resolution (not just mobile): lesson lists stay in a <details> closed by default, so the landing shows a dense grid of compact track cards that scan well — pairs with the new filter. Removed the viewport-sync script (no longer needed; closed by default everywhere). - Topic filter: a chip row (All / AI & LLMs / Distributed / Data & storage / APIs & design) filters cards by data-cat; integrates with search (clears it), re-applies after a search is cleared. Chips scroll horizontally on mobile. - Interaction states: :focus-visible outlines for keyboard a11y, :active tap feedback on chips/lesson-links/pills/results/buttons, hover+active on the Lessons toggle, is-active on the selected filter chip, chevron on open. - Fix CSS source-order bug: mobile card/grid/filter overrides were before the base rules (equal specificity -> base won), so they never applied. Moved them to the end block, after the base component rules. Also trimmed the hero on mobile so the filter + cards surface sooner. Verified: filter works (AI chip -> only AI cards), chips scroll on mobile, cards collapse on desktop+mobile, gates pass.
…lesson heading Lesson heading (mobile): h1 now clamp(1.45rem,6vw,2.2rem) + overflow-wrap so it scales down and never overflows on narrow phones; at <=540px the eyebrow/kicker and meta shrink with tighter letter-spacing and the h1 drops to clamp(1.45,7vw, 1.95rem) — fixes the oversized/awkward heading on small screens. Cards: added a keyword tag row to every track card (e.g. RAG · Retrieval · Embeddings) — scannable and indexable text for SEO. On mobile the description is hidden (still in the DOM for SEO) and the hero paragraph is line-clamped, so cards collapse to header + tags + Lessons toggle and ~3 fit per screen. Tags + description both show on desktop. All CSS/markup; verified at 500px (mobile rules) and desktop.
…ooter social - Tags cycle through six distinct hues (per-tag --tg) so each card's tag row reads multi-colour instead of one track colour. - Start button: a full-width primary CTA at the card BOTTOM (links to the track's first lesson) — moved out of the header to avoid clashing with long titles + the lesson-count badge. In-card collapsible lesson list hidden (kept in the DOM for SEO); header stays clean (icon + title + count). - Footer Connect links are a centered inline row (Twitter / X · Medium · LinkedIn · GitHub) instead of a vertical stack.
- Each filter chip now shows a count of tracks in that category
(All 9 · AI & LLMs 2 · Distributed 2 · Data & storage 3 · APIs & design 2),
computed from the cards so it can't drift.
- Start is now stateful: clicking it (or reading any lesson) records the track
in localStorage ('sd:progress'); on return the card's button becomes
'↻ Continue →' and links to the latest lesson the user was on. interview.js
(loaded on every lesson/bank page) updates the resume point as you read;
reference sheets/READMEs are skipped so Continue always lands on a lesson.
Degrades silently if storage is blocked.
Render each filter chip's track count as a small circular pill (inverted on the active chip for contrast) instead of plain dim text.
- Start/Continue button: was white-on-accent which failed WCAG AA on several tracks (teal/green/amber ~3.2-4.4:1) and badly in dark mode (light accents). Now light theme darkens the accent (white text passes AA) and dark theme uses the light accent with dark text. Reference 'Glossary/Resources' pills no longer use low-contrast --muted (now --ink, weight 500). Darkened light --muted (#6c6862 -> #5f5b54) so secondary text clears AA site-wide. - Resume banner: a dismissible 'Pick up where you left off — <track>' banner at the top of the hub, shown only when progress exists (reads sd:last + sd:progress recorded by interview.js), with a Continue button to the exact lesson. Verified light + dark renders; gates pass.
- Start/Continue (and the resume-banner Continue) now use the ink/bg pair — the same as the active 'All tracks' filter chip — instead of per-track accents: consistent across tracks and maximal contrast in both themes (dark button + light text in light mode; inverts in dark mode). - Fix card spacing: removed the redundant .tfoot margin-top (it stacked on the flex gap, creating a too-large gap before the reference pills) and the stray .start margin-top:auto, so section spacing is even across all cards and resolutions.
The tags and lesson-count badges used the pure hue as text on a faint tint, which fell below WCAG AA for the lighter hues in light mode and went low-contrast on the dark card in dark mode. Mix the hue toward the theme's --ink for the text (color-mix 70% hue + --ink) so each tag keeps its distinct colour but the label is readable in both themes; nudged the tint/border up slightly for definition.
- Start/Continue (cards + resume banner) now use the brand accent (indigo)
instead of flat black — consistent and accessible (white text light / dark
text on the lighter dark-theme accent).
- Fix the banner X: '.resume{display:flex}' was overriding the [hidden]
attribute (author rule beats UA [hidden]), so dismiss never hid it. Added
'.resume[hidden]{display:none}'. Dismissal now also persists per track
(sd:resume-x) so it doesn't reappear on reload.
- Banner redesign: message first, X moved to the top-right corner, cleaner
wrap on mobile.
The refresh icon and the message were separate flex items, so when the Continue button wrapped to its own line on narrow phones the icon ended up alone on a row above the text. Group icon + text in a .resume-msg flex unit so they always stay together; only the Continue button wraps below.
GitHub Pages serves .md as plain text (we use .nojekyll), so the Glossary and Resources links opened unreadable raw markdown. Convert each track's GLOSSARY.md / RESOURCES.md to a styled on-site HTML page (pandoc + the lesson design system: tokens.css, lesson.css, a hubbar, SEO meta) and point all links at the .html. Markdown stays the source of truth; scripts/build-reference-pages.py regenerates the HTML. Added the 8 pages to sitemap.xml.
Sum each track's per-lesson read times (from the lessons' meta) and show a '~Xh Ym read' line with a small clock glyph above the Start button — always visible on mobile and desktop. Kept it out of the header badge (which stays 'N lessons') to avoid crowding the title + tag row on small screens.
- Active filter chip used flat black (--ink) while everything else (Start, banner, etc.) is the indigo brand accent — switch it to var(--accent) so the palette is consistent (dark text on the lighter dark-theme accent). - Remove the per-card emoji icon squares for a cleaner, text-first card header (track colour identity still comes from the top border + tags).
The banner duplicated the card's Continue button. Removed it; instead the started track's card becomes the resume element: a 'Continue where you left off' eyebrow + accent ring, a 'Lesson N of M' progress bar (in place of the read-time line), the Continue button, and it floats to the top of the grid (most-recent track, from sd:last). More interactive and no duplication.
Distinguish resuming from starting: the in-progress card now uses a green 'continue' accent (eyebrow, ring, progress bar, and Continue button) while Start cards stay indigo. Light theme darkens the green so white button text clears AA; dark theme uses the lighter green token with dark text.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Type of change
Scope
Checklist
node scripts/check-broken-links.jspasses (no broken internal links)node scripts/validate-content.jspasses (SEO tags, interviewdata-levels, JSON-LD, sitemap in sync)docs/sitemap.xml<title>(· StackDepth), meta description, canonical, and Open Graph tagsFor content changes
data-levelvalues (core/senior/staff/design)Notes for the reviewer