refactor: Revamp Dakota page layout & styling#660
Conversation
Hoist fetch promise from dakota-versions.json into composables. Beneficial since I split the version labelling within the existing version badges and DakotaVersionCard component (styled like the variant card in homepage). With module-scope fetch, network request can be done effectively Assisted-by: MiMo V2.5 & Deepseek V4 Flash in OpenCode https://opncd.ai/share/AOUIIElO
since I moved it outside the cards
imo it'd fit into DakotaVersionCard better
- Bring the version + download card from homepage with Dakotaraptor as the character image - Move version badges & Alpha badge out of DakotaScene - Refactor column system: Put DakotaScene & DakotaHighlight on left, alpha badge + DakotaVersionCard + View on GitHub btn in right - Set mobile breakpoint to 840px - Capitalize NVIDIA Assisted-by: Deepseek V4 Flash & MiMo V2.5 in OpenCode https://opncd.ai/share/AOUIIElO
Add back blur backdrop bg, remove excess border, adjust gap between components + inner padding Assisted-by: Deepseek V4 Flash in OpenCode https://opncd.ai/share/AOUIIElO
Assisted-by: Deepseek V4 Flash in OpenCode https://opncd.ai/share/AOUIIElO
move .release-links CSS to DakotaApp and remove excess stylings in DakotaScene
to make both links wraps gracefully into a new line in mobile view (instead of only getting individual links wrap)
and remove extra margin-top on .brand-grid (inherited from global style)
add border to make it slightly more legible, then adjust margin + add min-width to keep the text within one line responsively
Swap blur background on DakotaScene div with an :after pseudo-element above the background in DakotaApp directly to highlight the hero section better. Also add text-shadow for hero title, description, and release announcement text Assisted-by: Deepseek V4 Flash in OpenCode https://opncd.ai/share/eX1mPPPt
- Move "Alpha" badge in top right side of DakotaVersionCard - Import IconGithubCircle from Material Design Icons by Iconify - Clean up CSS and make sure the badge wraps properly when needed Assisted-by: Deepseek V4 Flash in OpenCode https://opncd.ai/share/TCRveZsB
- Move Freedesktop SDK as second highlight - Clean up borders, use uniform font & icon size - Make download view div match version card height value on mobile breakpoints - Add underline effect when hovering Download ISO button Assisted-by: Deepseek V4 Flash in OpenCode https://opncd.ai/share/8UqM3fWo
and make height of card-box + download-view the same Assisted-by: Deepseek V4 Flash in OpenCode https://opncd.ai/share/faq6pdba
Assisted-by: Deepseek V4 Flash in OpenCode https://opncd.ai/share/Eh5FmK6P
including unused .brand-grid margin code & misleading `:download="entry.isoFilename"` attribute from the cross-origin download link Assisted-by: Deepseek V4 Flash in OpenCode https://opncd.ai/share/Eh5FmK6P
- Aligned breakpoints: min-width: 957px → 840px, max-width: 956px →
839px in DakotaHighlights
- Re-added .brand-grid { margin-top: 0 } inside the 839px block in
DakotaHighlights
- Added container-type: inline-size + @container (max-width: 380px) to
hide "Verify" label (DakotaVersionCard) and stack entry headers
vertically when cramped
Assisted-by: Deepseek V4 Flash in OpenCode
https://opncd.ai/share/Eh5FmK6P
|
Warning Review limit reached
More reviews will be available in 37 minutes and 36 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe PR restructures the Dakota product page from a text-focused single-column layout to a modern two-column design featuring a video/announcements section, a sticky version card component, and enhanced visual styling across related components. Version data loading is centralized through a new composable to eliminate redundant fetches. ChangesDakota page redesign
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (6)
src/components/dakota/DakotaVersionChips.vue (1)
119-132: 💤 Low valueConsider adopting mobile-first media queries.
The current media query uses
max-width: 500px(desktop-first approach), while the coding guidelines recommend "mobile-first responsive design approach" usingmin-widthbreakpoints. Although the current implementation works correctly, adopting mobile-first would improve consistency with project conventions.As per coding guidelines: "Use mobile-first responsive design approach; use IS_TABLET composable for conditional logic."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/dakota/DakotaVersionChips.vue` around lines 119 - 132, Switch the CSS in DakotaVersionChips.vue from a desktop-first max-width media query to a mobile-first min-width breakpoint: replace the `@media` (max-width: 500px) rules for .version-chips, .version-chip, .chip-label and .chip-value with a min-width-based breakpoint (e.g., apply base mobile styles outside any media query, then add an `@media` (min-width: 500px) block for larger-screen overrides). Also update any component logic that branches on viewport size to use the IS_TABLET composable (instead of relying on max-width CSS behavior) so the component follows the project's mobile-first convention.src/composables.ts (1)
20-31: ⚡ Quick winConsider adding runtime type validation for the JSON response.
The
res.json()call returnsPromise<any>, so TypeScript cannot verify that the fetched data matches theDakotaVersionsinterface at runtime. If the JSON schema changes or is malformed, this could cause silent failures or runtime errors in consuming components.♻️ Proposed fix using basic validation
function fetchVersionsOnce(): Promise<DakotaVersions> { if (!versionsPromise) { versionsPromise = (async () => { const res = await fetch(`${import.meta.env.BASE_URL}dakota-versions.json`) if (!res.ok) { throw new Error(`HTTP ${res.status}`) } - return res.json() + const data = await res.json() + if (!data || typeof data !== 'object' || !data.generatedAt || !data.packages) { + throw new Error('Invalid dakota-versions.json structure') + } + return data as DakotaVersions })() } return versionsPromise }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/composables.ts` around lines 20 - 31, The fetchVersionsOnce function currently returns res.json() (any) into versionsPromise typed as DakotaVersions; add runtime validation after awaiting res.json() to ensure the object matches the DakotaVersions shape and throw a descriptive error if not. Locate fetchVersionsOnce and versionsPromise, parse await res.json() into a local variable, validate required properties/types (either with a lightweight manual guard or a schema validator like zod/ajv) and only return the parsed value when it passes validation; keep the function signature as Promise<DakotaVersions> and ensure errors from invalid/malformed JSON propagate instead of returning unchecked any.src/components/dakota/DakotaVersionCard.vue (1)
22-44: 🏗️ Heavy liftHardcoded download host and
alpha2filenames will drift from the dynamic version data.The card displays live package versions from
getDakotaVersions(), but the ISO/checksum URLs are pinned toalpha2and a hardcodedBASEdomain. Each new release requires editing this file, and the download links can silently fall out of sync with the displayed versions. Consider deriving these from the versions JSON (or a config) so the download view tracks the current release.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/dakota/DakotaVersionCard.vue` around lines 22 - 44, The downloads are hardcoded via BASE and the static entries array, causing drift; instead remove the BASE const and static entries and build DownloadEntry objects from the fetched versions data (use the existing versions ref returned by getDakotaVersions()), e.g. derive base URL and current release filenames/checksum names from properties on versions (or versions.releases/latest) and populate entries dynamically when versions is populated; update any UI that references entries to use the computed/generated list so the ISO/checksum URLs follow the dynamic DakotaVersions data.src/DakotaApp.vue (1)
44-50: 💤 Low valueConsider lazy-loading the YouTube embed.
The iframe loads eagerly on mount. Adding
loading="lazy"(and optionally switching toyoutube-nocookie.com) avoids fetching the player before it scrolls into view, improving initial load.title="Dakota"is good for accessibility.⚡ Proposed tweak
<iframe - src="https://www.youtube.com/embed/v18c8ipK02A" + src="https://www.youtube-nocookie.com/embed/v18c8ipK02A" title="Dakota" frameborder="0" + loading="lazy" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/DakotaApp.vue` around lines 44 - 50, The YouTube iframe is loading eagerly—update the iframe element (the <iframe ... /> in DakotaApp.vue) to include loading="lazy" and consider switching the src host to "youtube-nocookie.com" (i.e., change the src URL to the privacy-enhanced embed) so the player is not fetched until it scrolls into view; keep the existing title attribute for accessibility.src/components/dakota/DakotaHighlights.vue (2)
178-178: 💤 Low valueConsider refactoring to avoid
!importantflags.Lines 178 and 196 use
!importantto set font sizes. While this may be necessary to override inherited specificity from global.brand-gridstyles, consider restructuring the selectors or adjusting specificity to avoid the!importantflag, which can make future maintenance more difficult.Also applies to: 196-196
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/dakota/DakotaHighlights.vue` at line 178, Remove the two uses of !important in DakotaHighlights.vue (the font-size rules at lines 178 and 196) by increasing selector specificity or scoping the styles: target the exact component class/element (e.g., a unique wrapper or element class inside DakotaHighlights.vue) or move the rules into the component's scoped <style> so they override global .brand-grid, and set font-size: 1.6rem (and the other size) normally; alternatively, adjust the global .brand-grid selector to be less specific so the component-level selector (e.g., .dakota-highlights .your-element or the component's scoped selectors) can win without !important. Ensure you update the selector names used in the file (component wrapper or element class) to the unique identifiers present in DakotaHighlights.vue so the new rules replace the !important declarations.
87-94: ⚖️ Poor tradeoffConsider using TailwindCSS utilities for spacing and visual effects.
Lines 87-90 use hardcoded pixel values for
padding,backdrop-filter, andborder-radius. As per coding guidelines, TailwindCSS utilities should be preferred for spacing and visual effects to maintain consistency and avoid hardcoding pixel sizes.While the current implementation achieves the desired visual effect, consider refactoring to use Tailwind classes in the template (e.g.,
p-6,backdrop-blur-md,rounded-xl) or Tailwind's@applydirective in SCSS for better alignment with project standards.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/dakota/DakotaHighlights.vue` around lines 87 - 94, Replace the hardcoded CSS in DakotaHighlights.vue's style block (the rules setting padding: 24px, backdrop-filter: blur(8px), and border-radius: 12px) with Tailwind utilities: move equivalent classes into the template wrapper (e.g., p-6, backdrop-blur-md, rounded-xl) or, if you need to keep styles scoped, use Tailwind's `@apply` inside the component's SCSS for the same selector (including :deep(.container) adjustments) so spacing and visual effects follow project Tailwind conventions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/dakota/DakotaScene.vue`:
- Line 23: The anchor in DakotaScene.vue with visible text "Freedesktop.org" but
href "https://freedesktop-sdk.io" has a label/URL mismatch; decide which is
intended and make them consistent—either change the anchor text to "Freedesktop
SDK" to match the existing href or change the href to
"https://www.freedesktop.org" to match the current label (update the <a> element
containing the freedesktop link accordingly).
In `@src/components/dakota/DakotaVersionCard.vue`:
- Around line 95-174: The template contains hardcoded user-facing strings (alpha
badge title/subtext, card subtitle "For the brave soul", card description "The
Final Form. Bluefin Perfected.", click hint "Click to download ↓", download
button "Download ISO", verify button "Verify", back button "← Back", etc. in
DakotaVersionCard.vue) which must be externalized to vue-i18n. Replace each
literal in the template with $t('dakota.<key>') (or the composition API
equivalent) and add corresponding keys in the locale JSON/YAML files under
src/locales/ (e.g., dakota.alphaTitle, dakota.alphaSub, dakota.subtitle,
dakota.description, dakota.clickHint, dakota.downloadButton,
dakota.verifyButton, dakota.backButton). Ensure DakotaVersionChips and dynamic
labels (entry.label, row.label) remain untouched if already localized upstream;
if not, feed them through $t where appropriate. Update any unit tests or
snapshots that assert on these exact strings.
- Around line 90-94: The clickable card in DakotaVersionCard.vue uses a
non-semantic <div class="card-box"> with only `@click`="openDownloads", which is
not keyboard/assistive-tech accessible; change the element to a semantic
<button> (or add role="button", tabindex="0", and a keydown handler that calls
openDownloads on Enter/Space) and ensure any visual styles from .card-box are
preserved so openDownloads is triggerable via keyboard and screen readers.
In `@src/composables.ts`:
- Around line 13-16: Export the DakotaVersions interface from composables.ts
(add an export before the interface declaration) and remove the duplicate local
interface in DakotaVersionChips.vue; instead import { DakotaVersions } from the
composables module and use that type where the local definition was used (update
any type references in DakotaVersionChips.vue to the imported DakotaVersions).
Ensure the symbol name DakotaVersions matches exactly and that the module import
path points to the composables file.
In `@src/DakotaApp.vue`:
- Around line 52-66: The template in DakotaApp.vue currently hardcodes
user-facing strings ("Read the announcements:", "Alpha 2 Release →", "Alpha 1
Release →") inside the release-links/release-link-item markup; replace those
literals with vue-i18n translation keys (use $t calls in the template) and add
matching keys to your locale files under src/locales/ (e.g., add
dakota.release.readAnnouncements, dakota.release.alpha2, dakota.release.alpha1
to en.json and other locale JSONs). Ensure the template uses the classed
elements (release-links, release-link-item) and the new $t keys so translations
load correctly, and run the app to verify the text is served from i18n.
---
Nitpick comments:
In `@src/components/dakota/DakotaHighlights.vue`:
- Line 178: Remove the two uses of !important in DakotaHighlights.vue (the
font-size rules at lines 178 and 196) by increasing selector specificity or
scoping the styles: target the exact component class/element (e.g., a unique
wrapper or element class inside DakotaHighlights.vue) or move the rules into the
component's scoped <style> so they override global .brand-grid, and set
font-size: 1.6rem (and the other size) normally; alternatively, adjust the
global .brand-grid selector to be less specific so the component-level selector
(e.g., .dakota-highlights .your-element or the component's scoped selectors) can
win without !important. Ensure you update the selector names used in the file
(component wrapper or element class) to the unique identifiers present in
DakotaHighlights.vue so the new rules replace the !important declarations.
- Around line 87-94: Replace the hardcoded CSS in DakotaHighlights.vue's style
block (the rules setting padding: 24px, backdrop-filter: blur(8px), and
border-radius: 12px) with Tailwind utilities: move equivalent classes into the
template wrapper (e.g., p-6, backdrop-blur-md, rounded-xl) or, if you need to
keep styles scoped, use Tailwind's `@apply` inside the component's SCSS for the
same selector (including :deep(.container) adjustments) so spacing and visual
effects follow project Tailwind conventions.
In `@src/components/dakota/DakotaVersionCard.vue`:
- Around line 22-44: The downloads are hardcoded via BASE and the static entries
array, causing drift; instead remove the BASE const and static entries and build
DownloadEntry objects from the fetched versions data (use the existing versions
ref returned by getDakotaVersions()), e.g. derive base URL and current release
filenames/checksum names from properties on versions (or
versions.releases/latest) and populate entries dynamically when versions is
populated; update any UI that references entries to use the computed/generated
list so the ISO/checksum URLs follow the dynamic DakotaVersions data.
In `@src/components/dakota/DakotaVersionChips.vue`:
- Around line 119-132: Switch the CSS in DakotaVersionChips.vue from a
desktop-first max-width media query to a mobile-first min-width breakpoint:
replace the `@media` (max-width: 500px) rules for .version-chips, .version-chip,
.chip-label and .chip-value with a min-width-based breakpoint (e.g., apply base
mobile styles outside any media query, then add an `@media` (min-width: 500px)
block for larger-screen overrides). Also update any component logic that
branches on viewport size to use the IS_TABLET composable (instead of relying on
max-width CSS behavior) so the component follows the project's mobile-first
convention.
In `@src/composables.ts`:
- Around line 20-31: The fetchVersionsOnce function currently returns res.json()
(any) into versionsPromise typed as DakotaVersions; add runtime validation after
awaiting res.json() to ensure the object matches the DakotaVersions shape and
throw a descriptive error if not. Locate fetchVersionsOnce and versionsPromise,
parse await res.json() into a local variable, validate required properties/types
(either with a lightweight manual guard or a schema validator like zod/ajv) and
only return the parsed value when it passes validation; keep the function
signature as Promise<DakotaVersions> and ensure errors from invalid/malformed
JSON propagate instead of returning unchecked any.
In `@src/DakotaApp.vue`:
- Around line 44-50: The YouTube iframe is loading eagerly—update the iframe
element (the <iframe ... /> in DakotaApp.vue) to include loading="lazy" and
consider switching the src host to "youtube-nocookie.com" (i.e., change the src
URL to the privacy-enhanced embed) so the player is not fetched until it scrolls
into view; keep the existing title attribute for accessibility.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 8657c2aa-319c-4c4d-942c-352a2dd3dd82
📒 Files selected for processing (6)
src/DakotaApp.vuesrc/components/dakota/DakotaHighlights.vuesrc/components/dakota/DakotaScene.vuesrc/components/dakota/DakotaVersionCard.vuesrc/components/dakota/DakotaVersionChips.vuesrc/composables.ts
add keydown prevent to Enter and Space press Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- src/composables.ts: Added export to interface DakotaVersions
- src/components/dakota/DakotaVersionChips.vue: Removed local duplicate
interface, now imports type { DakotaVersions } from ../../composables
(lint fix auto-split the type import)
Assisted-by: Deepseek V4 Flash in OpenCode
- Removed backticks from @click, @keydown.enter.prevent, @keydown.space.prevent (lines 95-97) — Vue template attributes must be plain, not backtick-quoted - openDownloads is now recognized as used by the template, fixing the unused-vars error Assisted-by: Deepseek V4 Flash in OpenCode
Comparison in desktop view
Screencast.From.2026-06-01.11-19-20.webm
Comparison in mobile view
Screencast.From.2026-06-01.11-37-50.webm
Highlights
Assisted-by: Deepseek V4 Flash & MiMo V2.5 in OpenCode
Summary by CodeRabbit
New Features
Style