Skip to content

feat: scaffold Astro + Vue migration (phases 0-1)#199

Open
sinduri-g wants to merge 48 commits into
mainfrom
feat/astro-migration
Open

feat: scaffold Astro + Vue migration (phases 0-1)#199
sinduri-g wants to merge 48 commits into
mainfrom
feat/astro-migration

Conversation

@sinduri-g

Copy link
Copy Markdown
Contributor
  • add nested astro/ app: static output, trailingSlash always, base from VITE_BASE_PATH
  • serve real public/ via publicDir; read real adventure YAML via glob content collection
  • port root.tsx head into Layout.astro: CSP, favicons, theme pre-hydration + after-swap reapply, GA4 consent bootstrap, dual JSON-LD, base-aware font preloads
  • add ClientRouter view transitions and native prefetch (replaces speculationrules script)
  • prove YAML -> Zod -> createMarkdownProcessor + rehype-sanitize field HTML at build time
  • port index.css (@theme) verbatim; wire Tailwind 4, astro-icon, Vue appEntrypoint
  • stub nanostores theme/consent persistent stores

Type of change

  • feat new feature
  • fix bug fix
  • refactor no behavior change
  • docs / chore / config / perf / style / security

Manual checks

  • Screen reader tested (UI changes only)
  • New routes added to sitemap.xml, prerender array, README (routes only)
  • UI verified at 375px, 768px, and 1280px against the production build (npm run build && npm run preview) (UI changes only)
  • Re-read every changed file; checked all call sites of any modified exports (all changes)
  • Per-level discussion JSON exists with correct discussionUrl (adventure/level changes only)

- add nested astro/ app: static output, trailingSlash always, base from VITE_BASE_PATH
- serve real public/ via publicDir; read real adventure YAML via glob content collection
- port root.tsx head into Layout.astro: CSP, favicons, theme pre-hydration + after-swap reapply, GA4 consent bootstrap, dual JSON-LD, base-aware font preloads
- add ClientRouter view transitions and native prefetch (replaces speculationrules script)
- prove YAML -> Zod -> createMarkdownProcessor + rehype-sanitize field HTML at build time
- port index.css (@theme) verbatim; wire Tailwind 4, astro-icon, Vue appEntrypoint
- stub nanostores theme/consent persistent stores

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://off-on-dev.github.io/website/pr-preview/pr-199/

Built to branch gh-pages at 2026-07-25 09:43 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

sinduri-g added 28 commits July 21, 2026 12:02
- add **/*.astro and **/*.vue globs to REUSE.toml MIT block (reuse-tool does not auto-recognize .astro, so reuse lint fails on the scaffold otherwise)
- exclude astro/** from root eslint; the nested app imports astro:* virtual modules that root tooling cannot resolve, and gets its own lint later

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…e 2)

- translate schemas/adventure.schema.json to a strict Zod schema (unknown fields fail the build, preserving the ajv validation gate)
- custom yaml-package loader instead of glob(): Astro's glob YAML parser auto-casts unquoted ISO deadlines to Date; the yaml package keeps them as strings, matching the generator. digest-gated
- port the generator's markdown pipeline verbatim (sanitize schema, abbr-tooltip expansion, external-link annotation, non-public-url unwrapping, md-inline/md-content)
- port field normalization: title/name, story, icon/emoji, difficulty/emoji, learnings aliases, intro/summary, codespacesUrl, discussionUrl, deadline, rewards defaults
- add verify-gate.mjs: renders real fields through the pipeline and diffs against committed *.generated.ts (155 matched, 0 mismatched)
- defer to phase 2b (TODO markers): architectureDiagram assets, meta-description synthesis, services step injection, solutions collection, discussion/leaderboard references

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…phase 2b)

- port buildAdventureMetaDescription/buildLevelMetaDescription; always emit metaDescription (synthesized when absent), matching the generator
- port services -> "Explore the UIs" how_to_play step injection
- extract pure derivations to src/lib/adventure-derive.mjs, shared by the loader and the verification gate
- extend verify-gate to cover metaDescription and the injected services step (191 matched, 0 mismatched)
- add npm scripts: sync (astro sync validation gate) and verify:content (byte-fidelity gate)
- reclassify solutions (pre-built TS) and discussion/leaderboard (refreshed JSON) out of the content collection; they become phase 3 import.meta.glob loads

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- add SEO.astro (canonical, OG incl article type, Twitter, fixed brand og:image); Layout delegates per-page meta to it
- add lib/site.ts (SITE_URL, BRAND_NAME, OG_IMAGE_ALT, canonicalUrl) and lib/markdown.ts (stripLinks, stripHtml, slugify ported)
- add /adventures/[id] detail route via getStaticPaths from the collection; prose via set:html with md-inline and stripLinks in link cards
- add /adventures list page (month-sorted, stripHtml story in cards)
- verified in dist: titles, canonical (trailing slash), OG/Twitter, synthesized descriptions, unescaped prose, trailing-slash routing

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…d (phase 3 increment 2)

- add /adventures/[id]/levels/[levelId] via getStaticPaths (all 17 level routes)
- render hook, intro, scenario, objective, toolbox, walkthrough, verification, helpful links, audience via set:html; abbr tooltip expansion confirmed on a real page
- load discussion posts and leaderboard at build time via node fs (community-data.ts) -> fully static, no client fetch
- resolve external data dir from process.cwd(), not import.meta.url: page-imported modules are bundled by Vite which rewrites import.meta.url

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…nt 3)

- add /challenges/[...tag] (base + 25 tag routes) via getStaticPaths from the collection
- add ChallengesFilter.vue island (client:load): SSR renders the full/pre-filtered grid so no-JS users get content, hydrates for topic/difficulty filtering
- URL-synced state via history.replaceState (scroll-preserving); hydration-safe seed from the route tag, restore ?topics/?difficulty in onMounted
- add lib/challenges.ts deriving the flat level list and tag set (mirrors filter-utils/tag-utils)
- solution page deferred: pre-built solution TS import from the React src via the @/ alias; bridge at cutover

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
… increment 4)

- add Navbar.astro (nav landmark, links, active state) and Footer.astro (Explore/Community landmarks, social, copyright)
- wire into Layout with skip-nav, new-tab-hint span, and focus-to-main on astro:after-swap
- add ThemeToggle.vue island (nanostores $theme + lucide-vue-next): hydration-safe, persists to localStorage, client:load + transition:persist
- replace placeholder home with a real landing (hero + featured adventures from the collection)
- add nav/site constants to lib/site.ts; remove scaffold smoke island

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…ment 5)

- port the full consent state machine from useConsent.tsx to stores/consent.ts: plain nanostores atom (hydration-safe, default null), React storage format ({value,timestamp}+180d expiry), gtag injector (queue update/js/config before appendChild, module-scoped once-guard), revoke, GPC auto-deny, _ga cookie clearing, firePageView on astro:page-load
- add ConsentBanner.vue (client:load + transition:persist): SSR renders the banner with aria-live; floating cookie-preferences button after a decision
- mount in Layout; gated load verified statically (zero gtag.js prerendered)
- NOTE: runtime transitions (accept/decline/GPC/re-grant/expiry) still need real-browser regression tests before merge

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…rement 6)

- port Hero.astro (fireflies, hero-badge, brand slogan/line, animate-fade-up) and AdventureCard.astro with sub-components DifficultyBadge, ContributorBadge, LivePill, AdventureIcon
- add lib/difficulty.ts and brand slogan constants
- use Astro class:list in place of cn; astro-icon (lucide, inline SVG, zero JS) in place of lucide-react
- home page uses Hero + card grid; /adventures uses the same cards; build-time isLive

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
… (phase 3 increment 7)

- port FilteredLevelCard design into ChallengesFilter.vue: card-glow, difficulty badge with dot, learnings, estimated-time pill, live pill, adventure icon
- inline badge/pill/icon markup with lucide-vue-next since .astro components can't render inside a Vue island
- extend challenges.ts ChallengeEntry with learnings, adventureIcon, and build-time isLive

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- port the two-column AdventureDetail layout: breadcrumb, header (title/icon/contributor badge/month/live pill/tag chips/story), main column (overview, challenge level cards, rewards, backstory), sidebar (leaderboard + contributor)
- add components: Breadcrumb, TagChips, InlineProse, PersonNameLink, RewardsCard, and lib/utils.ts (formatDeadline, isDeadlinePast)
- reuse ported AdventureIcon/DifficultyBadge/ContributorBadge/LivePill; build-time deadline gating for live pill and rewards placement

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- add src/pages/404.astro; Astro emits dist/404.html which GitHub Pages serves for unmatched paths as a real HTTP 404 (no soft-404 catch-all)

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…ook (phase 3)

- port four nav/footer-linked static pages to .astro with faithful content, correct SEO, and chrome via Layout
- add noindex prop to Layout/SEO; privacy is noindex (kept out of the index, matching the React page)
- notes: CommunityLeaders sidebar rendered as a build-time snapshot; abbr tooltip keeps CSS hover/focus, JS click/Escape toggle deferred

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- port About (board members + challenge builders), Sponsors (sponsor data + CSS theme-swapped logo), Brand Guidelines (all 8 sections), and Presentation Templates to .astro with faithful content
- copy sponsors.ts and team.ts data into astro/src/data/; add Dynatrace logos to public/brand/
- add redirects (/docs, /docs/community-guide, /community-guide -> /handbook/) via astro.config; presentation-templates is noindex
- nav and footer now have zero 404s; notes: brand TOC scroll-spy and community-leaders live refresh are static snapshots

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…(phase 3)

- port the ChallengeDetail two-column layout: header, objective/learnings cards, collapsible audience/story/architecture (native details, zero JS), synthetic Get Started walkthrough step, verification, toolbox/docs grid, share links, sidebar (compact rewards, codespaces, community sidebar, other levels)
- add components: CollapsibleSection, CodespacesButton, OtherLevelsCard, ChallengeShareLinks, CommunitySidebar; extend RewardsCard with the compact variant
- wire architectureDiagram SVGs (copied to astro/src/assets/diagrams, matched by filename) with architectureAscii fallback
- content gate still 191 matched, 0 mismatched

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- bridge the React src solution modules with two exact-find Vite aliases (@/data/solutions/types, @/data/adventures/contributors); astro's own @/ alias unaffected
- add lib/solutions.ts (import.meta.glob over src/data/solutions) and the /adventures/[id]/levels/[levelId]/solution route with getStaticPaths over authored solutions only
- port the SolutionDetail layout: spoiler warning, step nav, context card, details step cards with takeaways, block renderer (text/code/image/callout), final result, outro, sidebar
- build-time deadline gating with locked-state fallback (noindex)

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…hase 5)

- add Playwright + axe a11y suite (e2e/a11y.spec.ts) over 15 representative routes: axe dark/light/forced-colors with the full WCAG tag set, touch targets (2.5.8), focus rings dark/light, focus traps, 200% zoom reflow. 105 tests pass
- reuseExistingServer:false so tests never run against a stray dev server
- fix heading order: sr-only h2 before card grids on /adventures and /challenges
- fix touch targets: nav links min-h-[44px], footer links min-h-[48px], clear-filters button min-h-6
- fix 200% zoom reflow: abbr tooltip ::after uses display:none when hidden (was opacity:0, which kept layout and extended page width); the React JS tooltip that suppresses this CSS is a follow-up
- constrain walkthrough step grid track with minmax(0,1fr) so code blocks scroll internally

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- add e2e/smoke.spec.ts: every route has a unique correct <title>, canonical (SITE_URL+path), meta description, matching og:title/og:url, exactly one <h1>, and no page errors
- island hydration checks: theme toggle switches theme + sets .light class; challenges filter filters and syncs ?difficulty
- 19 tests pass

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…tover stage 1)

- deploy.yml: output dist/ (not dist/client/), drop the 404.html copy (astro emits dist/404.html natively), keep JamesIves/github-pages-deploy-action
- preview.yml: check job runs sync + verify:content gates; build/e2e/preview use dist/; e2e runs playwright (webServer serves via astro preview); preview build uses astro base (VITE_BASE_PATH) so public assets deploy from dist/ with no per-directory copy step
- these target the post-move root structure; inert on the branch until the cutover move lands and the branch is merged
- TODO(cutover): add npm run lint once eslint-astro/vue config is ported

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…e (stage A)

- copy adventure YAML, discussion/leaderboard JSON, solutions, contributors.ts and types.ts into astro/src/data (the app no longer reads the React src/data tree)
- re-point content.config (data/adventures), community-data (cwd/src/data/adventures), and solutions (../data/solutions) to the local copies
- drop the two interim Vite aliases; @/data/solutions/types and @/data/adventures/contributors now resolve via the app's @/ -> src alias
- build green (63 pages), content gate 191/0, all 124 e2e tests pass

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…age B)

BREAKING: the React Router app is removed; offon.dev now builds from Astro.

- delete the React app: src/ (root.tsx, entry.*, routes.ts, ~50 .tsx pages/components, hooks), react-router.config.ts, serve.json, the React vite/vitest/eslint/tsconfig configs, e2e/, and the shadcn components.json
- delete the content generator: scripts/generate-adventures.mjs, generate-solutions.mjs, create-data-aliases.mjs, schemas/adventure.schema.json, and all *.generated.ts / index.ts / summaries.ts (replaced by the Zod content collection)
- move the Astro app from astro/ to the repo root (src/, astro.config.mjs, package.json, tsconfig.json, e2e/, playwright.config.ts)
- drop publicDir override (public/ is now local); remove the obsolete verify:content gate (nothing left to diff against) and its verify-gate.mjs
- preview.yml: content gate is now `astro sync` only
- kept: public/, refresh-*/sync/community-sitemap/check-docs scripts, docs, LICENSES, REUSE.toml
- verified at root: build (63 pages), astro sync, 124 e2e tests (105 a11y + 19 smoke) all pass
- follow-ups: rewrite sync-adventure/add-discussion-url/validate-adventures workflows (still reference the removed generator), add eslint-astro/vue lint, custom sitemap endpoint, update CLAUDE.md/README/REUSE.toml, port abbr JS tooltip + Shiki highlighting + click tracking

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…tover follow-up)

- replace the deleted `generate-adventures.mjs --validate-only` with `npm run sync` (astro sync runs the Zod content schema and fails on invalid adventure YAML)
- drop the obsolete checks: generated TS existence, index.ts import, react-router prerender entries, and sitemap.xml grep (routes now come from getStaticPaths; sitemap endpoint is a follow-up)
- keep: per-level discussion JSON existence and ADVENTURE_CATEGORIES registration in refresh-leaderboard.mjs
- update path triggers (src/content.config.ts instead of schemas/react-router.config)

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…nerator (cutover follow-up)

- replace `node scripts/generate-adventures.mjs` with `npm run sync` (Zod validation) in both workflows
- commit only the collection inputs (adventure.yaml, *-posts.json, diagrams, refresh-leaderboard.mjs); stop staging the removed *.generated.ts / index.ts / summaries.ts
- rewrite PR-body prose: routes/sitemap now come from getStaticPaths at build time (nothing to regenerate); checks use `npm run sync && npm run build && npm run test:e2e`

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…llow-up)

- replace all React Router / generator guidance with Astro: stack, repo layout, commands, content collection, components/islands, hydration safety, SEO (<SEO> component), deployment (dist/, trailingSlash, PR-preview base)
- rewrite the analytics/consent section for the nanostores $consent store + Vue island (state machine preserved); update constants home to src/lib/site.ts
- preserve framework-agnostic rules verbatim: Content & Copy, Git, Brand, URLs, Stability, Debugging evidence rules
- add a Known follow-ups section (lint, sitemap endpoint, abbr JS, Shiki, click tracking, REUSE cleanup)

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…llow-up)

- README: Astro + Vue stack, commands (astro build/sync/test:e2e, dev port 4321), project structure, content collection, routes (.astro + getStaticPaths), SEO (<SEO> component), analytics (nanostores), deployment (dist/, base)
- REUSE.toml: drop entries for deleted files — *.generated.ts (block 3 + per-contributor blocks), src/components/ui + shadcn block, and community-leaders.json (CC0 block); src/lib/utils.ts now falls under the MIT **/*.ts glob

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…tion (cutover follow-up)

- ADVENTURES.md: rewrite the content pipeline (YAML -> content collection, no generation), the sync-workflow steps, the solutions flow (import.meta.glob, no barrel), the checks command (npm run sync && build && test:e2e), the validate-adventures row, and the constants pointer (src/lib/site.ts)
- PERFORMANCE.md: add a migration note (Layout.astro preloads, Astro islands code-splitting, content-collection markdown, native prefetch, dist/ output, getStaticPaths) — principles unchanged
- styleguide.md: add a migration banner (design system stays authoritative; component entries describe the former React impl; .astro/.vue components are source of truth). Full component rewrite tracked as follow-up

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…follow-up)

- add eslint.config.js (flat): @eslint/js + typescript-eslint + eslint-plugin-astro + eslint-plugin-vue; ignore dist/.astro/node_modules/public/.claude; quiet purely-stylistic vue formatting rules; keep no-unused-vars as error
- pin typescript to 6.x (typescript-eslint does not support TS 7 yet); build still green on TS 6
- add `npm run lint` script and run it in preview.yml's check job (removes the cutover TODO)
- fix the two real lint errors: unused `base` in the level page and unused `SITE_URL` import in brand.astro

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…r follow-up)

- add src/pages/sitemap.xml.ts endpoint: enumerates home, /adventures/, adventure + level routes, solution routes, /challenges/ + tag routes, and the indexable static pages, with a build-date lastmod
- exclude /privacy/, /presentation-templates/ (both noindex), and /404/
- delete the stale hand-maintained public/sitemap.xml (the generator that patched it is gone); /sitemap.xml is now generated at build (60 URLs)
- robots.txt, api-catalog, and the Layout sitemap link all reference /sitemap.xml, served by the endpoint

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
sinduri-g added 19 commits July 24, 2026 13:29
Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…follow-up)

- add e2e/consent.spec.ts: routes googletagmanager to an empty stub (no real GA network) and asserts the observable effects of each transition
- covers: no gtag/banner before consent; Accept injects gtag.js + stores granted + swaps to the cookie button; Decline stores denied with no gtag; Cookie Preferences resets to undecided; stored granted re-injects on load; stored denied stays silent
- 6 tests pass; verifies the gated-load guarantee end-to-end

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- add trackClicks() to the consent store: one capture-phase delegated listener (bound once, survives view transitions) that fires gtag click_event for a[href]/button clicks only when consent is granted
- wire it in ConsentBanner.vue onMounted alongside initConsent + page_view
- add an e2e test: no click_event before consent, click_event after Accept (7 consent tests pass)

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…m main

- add dead-reckoning "The Chronometer" expert level (adventure.yaml), its expert-posts.json and architecture diagram, drop the upcoming_levels placeholder
- carry over <abbr> expansions across all six adventure YAMLs (origin/main content superset, verified abbr-only delta)
- flip has_expert to true for dead-reckoning in refresh-leaderboard.mjs
- register the expert level route in the smoke + a11y test lists

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- Navbar: fixed top-0 z-50 full-width bar, theme-swapped image logo (was text), max-w-6xl, animated underline nav links with active state, Community external link with icon in original order
- add MobileMenu.vue island: hamburger + drawer with focus trap, Escape, and inert/aria-hidden on body siblings (ports the React useFocusTrap/useEscapeKey behaviour); drawer positioned absolute top-full under the fixed nav
- ThemeToggle.vue: bordered 44x44 control with desktop/mobile variants (was borderless p-2)
- Footer: image logo, Zap-separated slogan strip, LinkedIn/Bluesky/X social icon buttons (were text links), "All rights reserved" line, max-w-6xl two-column grid
- bump detail-page main padding pt-12 to pt-28 so content clears the now-fixed nav
- all 139 e2e checks pass (axe dark/light/forced-colors, touch targets, focus rings, zoom, consent)

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- add ChallengeHighlights, SponsorStrip, BottomCTA (with Nyx mascot), CommunitySection + CommunityLeaders, SectionLabel, AvatarLink as .astro components
- restore the "Got an Idea for an Adventure?" primary CTA band
- restore community-leaders.json (refreshed hourly by the existing refresh-community-data workflow)
- home grid: SectionLabel eyebrow, "Choose Your Adventure" heading, adventure/challenge counts, gap-5 grid, limit 6 with See-all fallback (matches React unfiltered ChallengesGrid)
- interactive filtering + StarterNudge deferred to the shared filter island (Phase 5)
- all 139 e2e checks pass

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- add PageHero.astro (shared primary hero, pt-32 clears fixed nav) and ChallengeBuildersSection.astro
- Adventures page: PageHero, How Adventures Work 3-column, All Adventures heading with count + filter link, ChallengeBuildersSection with CommunityLeaders aside, BottomCTA (was a bare card grid)
- reuse existing PersonNameLink.astro + InlineProse.astro + ADVENTURE_CONTRIBUTORS from data/team
- all 139 e2e checks pass

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…es grids

- rebuild ChallengesFilter.vue to the React ChallengeFilters design: desktop difficulty-colored pill rows + All Levels/All Tools reset pills + active X icons; mobile/tablet dropdowns with click-outside, Escape, arrow-key nav, colored swatches, and check marks; min-h-[44px] touch targets
- unfiltered view now shows AdventureCards (passed as an Astro slot, since .astro cannot render inside a Vue island) and switches to level cards when filtered, with the live region + count lines
- rebuild Challenges page: PageHero, StarterNudge, filter, ChallengeBuildersSection + CommunityLeaders aside, BottomCTA (max-w-6xl); was a bare max-w-4xl main
- add StarterNudge.vue island; wire the interactive filter + StarterNudge into the home Choose Your Adventure grid (embedded, See-all when adventures exceed the preview)
- add font-semibold to the level-card difficulty badge (matches the site-wide badge weight)
- update the smoke filter test for the adventure/level result toggle
- all 139 e2e checks pass

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- DifficultyBadge: add font-semibold (was dropped site-wide, rendered normal weight)
- 404 page: restore the 3-card Helpful links grid (Handbook/Challenges/About), title-case heading, and noindex (was a stripped minimal page, indexable)
- adventure detail: wrap Overview and The Story in CollapsibleSection cards (were plain static headings); upgrade the sidebar leaderboard to avatar rows via new LeaderboardList.astro (was a no-avatar text list); sidebar sticky offset lg:top-8 to lg:top-28
- level page: make each walkthrough step an individually collapsible <details> with a chevron (was static/expanded); add the "Check Out the Solution Walkthrough" CTA card (post-deadline, when a solution exists)
- all 139 e2e checks pass

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- add a progressive-enhancement script in Layout that adds a language-label header and a Copy button to every code block, ported from the React MarkdownContent/CodeBlock DOM injection
- prose code blocks get a full code-block-header bar; solution code blocks reuse their existing title bar and gain a Copy button
- runs on astro:page-load so it re-applies after View Transitions; uses the existing (previously dead) .code-block-header/.code-lang-label/.code-header-btn CSS
- verified at runtime: 15 copy buttons on a solution page, 5 buttons + 5 labels on a prose level page
- all 139 e2e checks pass

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- inventory the components after the visual-parity restoration (chrome, landing sections, filter island, cards/atoms) so the guide reflects the current .astro/.vue source ahead of the full component-by-component rewrite (still a tracked follow-up)

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- code-block enhancer: label prose blocks with the real fenced language (read code.class="language-*", was always "code"); add a polite live region announcing "Code copied to clipboard" (a focused button's aria-label change is not reliably re-announced) — both restore React MarkdownContent/CodeBlock parity
- level page: gate the solution CTA on the solution-unlock predicate (!deadline || isDeadlinePast) instead of rewardsBelowFold, so a no-deadline level with a solution links to its (reachable) solution page
- ChallengesFilter: clearing all tags on a /challenges/:tag/ route now drops the path segment to /challenges/, so the filter no longer re-seeds from the tag on reload/share/back
- ThemeToggle: subscribe to the theme store post-mount so the desktop and mobile instances stay in sync (fixes a stale icon + dead first-click after toggling then crossing the md breakpoint); kept hydration-safe (no store read at render)
- MobileMenu: force-close the persisted drawer on astro:before-swap (a logo/back navigation could otherwise leave it open with a live focus trap) and on crossing to >=md width
- CommunityLeaders: guard the section icon lookup against an unmapped id
- verified at runtime: prose labels show "sh", tag-clear drops the path, live region present up front; all 139 e2e pass

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…allenges

- add sortAdventuresByMonthDesc and isAdventureLive to src/lib/challenges.ts
- replace the copy-pasted MONTHS/monthKey/isLive blocks in index.astro, adventures/index.astro, and challenges/[...tag].astro (per CLAUDE.md: shared derivations live in src/lib, not re-derived ad hoc in pages)
- behavior-preserving; all 139 e2e pass

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
Brings the branch up to date with main so the migration PR is conflict-free.
All conflicts were mechanical and resolved deterministically:

- kept the branch (Astro) versions of CLAUDE.md, ADVENTURES.md, e2e/smoke.spec.ts, package-lock.json
- kept deleted the React-era files main still edits: the six *.generated.ts, summaries.ts, ChallengeDetail.tsx, react-router.config.ts, generate-adventures.mjs, prerender.test.ts, seo.test.ts, and public/sitemap.xml (the branch generates the sitemap via src/pages/sitemap.xml.ts)
- auto-merged the incoming community-data refreshes, community-sitemap.xml, and the sync-adventure.mjs local-diagram fallback

Adventure YAML, the expert diagram, and community-leaders were already absorbed, so no content conflicts arose.

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- port the React useAbbrTooltips behaviour as a Layout client enhancer: replace the CSS ::after tooltip with a position:fixed portal appended to <body>
- fixes two mobile bugs: the tooltip no longer overflows the viewport right edge (clamped so its right edge stays within the viewport), and it no longer gets clipped/cut off by overflow ancestors in walkthrough cards and scroll containers (it renders at body level, escaping them), so the full expansion text is shown
- tap forces focus (iOS), Escape dismisses, hoverable bridge preserved; one shared scroll/resize handler repositions the visible tip; the CSS ::after remains the no-JS fallback (suppressed via abbr-js-tooltip once the portal is live)
- verified at 375px: tips stay within the viewport, unclipped, full text wraps; all 139 e2e pass
- clears the abbr JS tooltip known follow-up in CLAUDE.md

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
…w-ups

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- the cutover 'prune stale REUSE entries' commit removed the CC0-1.0 block for src/data/community-leaders.json, leaving LICENSES/CC0-1.0.txt unused (REUSE lint failure) and silently relicensing the scraped Discourse data as MIT via the **/*.json glob
- re-add the annotation (placed last so it overrides the glob), matching origin/main: usernames/avatars/counts are factual scraped data, dedicated to the public domain under CC0-1.0
- verified locally: reuse lint passes (Unused licenses: 0; Used: CC-BY-4.0, CC0-1.0, MIT, OFL-1.1)

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
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