Skip to content

pivot: landing#6

Merged
notxorand merged 5 commits intomainfrom
pivot
Apr 28, 2026
Merged

pivot: landing#6
notxorand merged 5 commits intomainfrom
pivot

Conversation

@notxorand
Copy link
Copy Markdown
Member

@notxorand notxorand commented Apr 14, 2026

Greptile Summary

This PR redesigns the landing page with new content sections, introduces four use-case pages, centralises all content inside a max-w-3xl container with decorative side borders, adds structured-data LD+JSON, security headers, prerender config, and a server-side sitemap.

Two items worth addressing before merge: all four cards in Ribbon.vue share an identical id (navigation-top-left-bottom-right), producing duplicate DOM IDs; and --font-sans / --font-mono were moved out of @theme static into :root, which may cause Tailwind v4 to stop recognising them as design tokens and fall back to system fonts.

Confidence Score: 4/5

  • Safe to merge after addressing the duplicate-ID and @theme font-token issues.
  • Two P2 findings — duplicate DOM IDs in Ribbon.vue and font tokens moved out of @theme — could cause subtle rendering or accessibility problems in production. Neither is catastrophic, but both are worth a quick fix before shipping.
  • app/components/Ribbon.vue (duplicate IDs) and app/assets/style.css (font tokens outside @theme)

Important Files Changed

Filename Overview
app/components/Ribbon.vue Refactored from a static tagline into a use-case grid; all four NuxtLink cards share an identical DOM id, producing invalid HTML.
app/assets/style.css Adds Lilex font-face and swaps font-mono from "Intel" to "Lilex"; moves --font-sans and --font-mono out of @theme static into :root, which may prevent Tailwind v4 from registering them as design tokens.
nuxt.config.ts Substantial SEO additions: structured-data LD+JSON scripts, meta tags, security headers, prerender routes, and SWR cache rules — all look correct.
server/routes/sitemap.xml.ts New server-side sitemap covering static routes; dynamic blog/docs slugs are not included, and lastmod is build-time which is acceptable given prerendering.
app/pages/index.vue Landing page expanded with "Problem / How It Works / Built For / Get Started" sections and improved SEO meta; commented-out Overview block is minor dead code.
app/layouts/default.vue Content now centred inside a max-w-3xl container with fixed decorative side borders; clean and correct.
app/components/Navigation.vue Fixed bottom navbar refactored to respect the new max-width layout; menu now closes on link click via @click="toggleMenuState".
app/pages/usecases/business-logic.vue New use-case page; content and structure are consistent with the other three use-case pages.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[default.vue layout] --> B[Navigation.vue fixed bottom navbar]
    A --> C[main slot max-w-3xl centred]
    A --> D[Footer.vue]

    C --> E[pages/index.vue Hero + sections + Ribbon]
    C --> F[pages/roadmap.vue]
    C --> G[pages/blog/index.vue]
    C --> H[pages/blog/slug.vue]

    E --> I[components/Hero.vue]
    E --> J["components/Ribbon.vue — duplicate IDs"]

    J --> K[usecases/real-time-pipelines]
    J --> L[usecases/event-workflows]
    J --> M[usecases/state-management]
    J --> N[usecases/business-logic]

    O[docs.vue layout] --> P[DocsNavigation.vue]
    O --> Q[main slot max-w-3xl centred]
    Q --> R[pages/docs/index.vue]
    Q --> S[pages/docs/slug.vue]

    T[nuxt.config.ts prerender + SEO] --> U["server/routes/sitemap.xml.ts static routes only"]
    T --> V["public/robots.txt conflicts with server route"]
Loading

Comments Outside Diff (5)

  1. nuxt.config.ts, line 2648-2652 (link)

    P1 Global canonical points every page to the homepage

    Setting a static canonical href to https://slung.tech in the global app.head config injects <link rel="canonical" href="https://slung.tech"> on every page — including /blog/my-post, /docs/getting-started, etc. Search engines will interpret this as every page being a duplicate of the homepage, causing all non-root pages to be deindexed in favour of /. No individual page in this PR overrides this link via useHead.

    Either remove this global canonical and set it per-page (using useHead or useSeoMeta with the dynamic route.fullPath), or dynamically compute the href:

    // in a composable or layout, not in static nuxt.config
    useHead({
      link: [{ rel: "canonical", href: `https://slung.tech${useRoute().path}` }],
    });
  2. public/site.webmanifest, line 2921-2935 (link)

    P1 Duplicate screenshots key in web manifest

    The screenshots array is declared twice at the top level of the JSON object (once at line ~2843 and again here). The JSON spec treats duplicate keys as undefined behaviour; most parsers silently use the last value, dropping the first declaration entirely. This means the form_factor: "narrow" and form_factor: "wide" entries from the first block are lost. Remove the duplicate declaration and merge any unique entries into a single array.

  3. nuxt.config.ts, line 2716-2735 (link)

    P2 SearchAction targets a non-existent /search route

    The WebSite JSON-LD schema includes a SearchAction with urlTemplate: "https://slung.tech/search?q={search_term_string}". No /search page exists in this codebase, so Google's Sitelinks search box feature won't work and any crawler that follows the template will hit a 404. Either remove the potentialAction block or implement the search page first.

  4. app/composables/useOpenGraph.ts, line 663 (link)

    P2 datePublished always reflects the current time

    datePublished: new Date().toISOString() is evaluated at render time, so every server-side render reports the page as published right now. For blog posts this is semantically wrong — search engines expect datePublished to be the original publication date and dateModified for updates. The composable already receives its parameters at call-site, so datePublished should be an explicit parameter with a sensible fallback only when unavailable.

  5. app/components/Ribbon.vue, line 562-591 (link)

    P1 All use-case cards share the same HTML id

    The four entries in the useCases array all carry the same id value. The template forwards this as both the element id attribute and the v-for key, so every rendered NuxtLink gets an identical id (invalid HTML — only the first is reachable via getElementById) and Vue will warn about duplicate keys, which can cause incorrect virtual-DOM patching. Each item needs a distinct id value.

Reviews (3): Last reviewed commit: "fix: more" | Re-trigger Greptile

Comment thread server/routes/robots.txt.ts
@koyeb koyeb Bot temporarily deployed to production-evident-salamander/web April 14, 2026 08:46 Inactive
@koyeb koyeb Bot temporarily deployed to production-evident-salamander/web April 14, 2026 08:55 Inactive
@koyeb koyeb Bot temporarily deployed to production-evident-salamander/web April 14, 2026 09:09 Inactive
@koyeb koyeb Bot temporarily deployed to production-evident-salamander/web April 14, 2026 09:17 Inactive
@koyeb koyeb Bot temporarily deployed to production-evident-salamander/web April 25, 2026 01:07 Inactive
@railway-app railway-app Bot temporarily deployed to slunghq / production April 25, 2026 01:07 Inactive
@koyeb koyeb Bot temporarily deployed to production-evident-salamander/web April 28, 2026 00:06 Inactive
@notxorand notxorand merged commit 02fad06 into main Apr 28, 2026
0 of 2 checks passed
@notxorand notxorand deleted the pivot branch April 28, 2026 00:08
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