Skip to content

Wireframes Alignment Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Align the current UI to the wireframes concept — add full footer, breadcrumbs on product page, compact category grid, and hero divider styling.

Architecture: New AppFooter widget (FSD widget layer), new useProductBreadcrumbs composable (FSD feature layer), visual tweaks to existing homepage components. All text via i18n. All sizes in rem, using SCSS functions where applicable.

Tech Stack: Nuxt 4.3, Vue 3.5, Nuxt UI v3, Tailwind CSS 4, SCSS abstracts (px-to-rem, fluid-type), @nuxtjs/i18n


Files:

  • Modify: i18n/locales/ru.json

Step 1: Add footer translation keys to ru.json

Add a new "footer" section after the "error" section at line 494:

json
"footer": {
  "brand": {
    "title": "PARTIZAP",
    "description": "Маркетплейс автозапчастей в Санкт-Петербурге"
  },
  "buyers": {
    "title": "Покупателям",
    "catalog": "Каталог",
    "howToBuy": "Как купить",
    "paymentDelivery": "Оплата и доставка",
    "guarantees": "Гарантии"
  },
  "sellers": {
    "title": "Продавцам",
    "createListing": "Разместить объявление",
    "tariffs": "Тарифы",
    "rules": "Правила"
  },
  "company": {
    "title": "Компания",
    "about": "О нас",
    "contacts": "Контакты",
    "privacy": "Политика конфиденциальности"
  },
  "contacts": {
    "title": "Контакты",
    "email": "info{'@'}partizap.ru",
    "phone": "+7 (812) 000-00-00",
    "city": "г. Санкт-Петербург"
  },
  "copyright": "© {year} Partizap. Все права защищены."
}

Step 2: Verify dev server still works

Run: npm run dev (check no i18n parse errors in console)

Step 3: Commit

bash
git add i18n/locales/ru.json
git commit -m "feat(DEV-200): add footer i18n keys"

Task 2: Create AppFooter widget

Files:

  • Create: app/widgets/footer/ui/AppFooter.vue

Step 1: Create the AppFooter component

vue
<template>
  <footer class="footer">
    <div class="container-wide footer__grid">
      <!-- Brand -->
      <div class="footer__section">
        <h4 class="footer__title">{{ t('footer.brand.title') }}</h4>
        <p class="footer__text">{{ t('footer.brand.description') }}</p>
      </div>

      <!-- Buyers -->
      <div class="footer__section">
        <h4 class="footer__title">{{ t('footer.buyers.title') }}</h4>
        <ul class="footer__list">
          <li>
            <NuxtLink to="/catalog" class="footer__link">{{ t('footer.buyers.catalog') }}</NuxtLink>
          </li>
          <li>
            <NuxtLink to="#" class="footer__link">{{ t('footer.buyers.howToBuy') }}</NuxtLink>
          </li>
          <li>
            <NuxtLink to="#" class="footer__link">{{ t('footer.buyers.paymentDelivery') }}</NuxtLink>
          </li>
          <li>
            <NuxtLink to="#" class="footer__link">{{ t('footer.buyers.guarantees') }}</NuxtLink>
          </li>
        </ul>
      </div>

      <!-- Sellers -->
      <div class="footer__section">
        <h4 class="footer__title">{{ t('footer.sellers.title') }}</h4>
        <ul class="footer__list">
          <li>
            <NuxtLink to="/cabinet/products/new" class="footer__link">
              {{ t('footer.sellers.createListing') }}
            </NuxtLink>
          </li>
          <li>
            <NuxtLink to="#" class="footer__link">{{ t('footer.sellers.tariffs') }}</NuxtLink>
          </li>
          <li>
            <NuxtLink to="#" class="footer__link">{{ t('footer.sellers.rules') }}</NuxtLink>
          </li>
        </ul>
      </div>

      <!-- Company -->
      <div class="footer__section">
        <h4 class="footer__title">{{ t('footer.company.title') }}</h4>
        <ul class="footer__list">
          <li>
            <NuxtLink to="#" class="footer__link">{{ t('footer.company.about') }}</NuxtLink>
          </li>
          <li>
            <NuxtLink to="#" class="footer__link">{{ t('footer.company.contacts') }}</NuxtLink>
          </li>
          <li>
            <NuxtLink to="#" class="footer__link">{{ t('footer.company.privacy') }}</NuxtLink>
          </li>
        </ul>
      </div>
    </div>

    <!-- Bottom bar -->
    <div class="footer__bottom">
      <div class="container-wide footer__bottom-inner">
        {{ t('footer.copyright', { year: new Date().getFullYear() }) }}
      </div>
    </div>
  </footer>
</template>

<script setup lang="ts">
const { t } = useI18n()
</script>

<style lang="scss" scoped>
.footer {
  background-color: var(--ui-bg-inverted);
  color: var(--ui-text-inverted);
  padding-top: px-to-rem(40px);
}

.footer__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: px-to-rem(32px);

  @include media-down(md) {
    grid-template-columns: repeat(2, 1fr);
    gap: px-to-rem(24px);
  }

  @include media-down(sm) {
    grid-template-columns: 1fr;
    gap: px-to-rem(20px);
  }
}

.footer__title {
  font-size: $font-size-sm;
  font-weight: $font-weight-semibold;
  text-transform: uppercase;
  letter-spacing: $letter-spacing-wide;
  margin-bottom: px-to-rem(12px);
}

.footer__text {
  font-size: $font-size-xs;
  opacity: 0.7;
  line-height: $line-height-relaxed;
}

.footer__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: px-to-rem(8px);
}

.footer__link {
  font-size: $font-size-xs;
  opacity: 0.7;
  text-decoration: none;
  color: inherit;
  transition: opacity $transition-fast;

  &:hover {
    opacity: 1;
  }
}

.footer__bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.15);
  margin-top: px-to-rem(32px);
  padding: px-to-rem(16px) 0;
}

.footer__bottom-inner {
  font-size: $font-size-xs;
  opacity: 0.6;
  text-align: center;
}
</style>

Step 2: Verify in browser

Check: Footer not yet visible in layout (we'll wire it in Task 3).

Step 3: Commit

bash
git add app/widgets/footer/ui/AppFooter.vue
git commit -m "feat(DEV-200): create AppFooter widget with 4-column grid"

Task 3: Wire AppFooter into default layout

Files:

  • Modify: app/layouts/default.vue

Step 1: Replace the inline footer with AppFooter component

Replace the current default.vue content with:

vue
<template>
  <div class="min-h-screen flex flex-col">
    <AppHeader />

    <main class="flex-1">
      <slot />
    </main>

    <AppFooter />
  </div>
</template>

The old <footer> block (lines 8-12) is completely removed and replaced with <AppFooter />.

Step 2: Verify in browser

  • Open http://localhost:3000
  • Scroll down — should see dark 4-column footer with all sections
  • Check mobile (resize or DevTools): 2 columns on tablet, 1 column on mobile
  • Check dark mode: footer should still look correct

Step 3: Commit

bash
git add app/layouts/default.vue
git commit -m "feat(DEV-200): wire AppFooter into default layout"

Task 4: Create useProductBreadcrumbs composable

Files:

  • Create: app/features/product-breadcrumbs/composables/useProductBreadcrumbs.ts

Step 1: Implement the composable

This composable takes the product's category chain and builds a breadcrumb array for <UBreadcrumb>:

typescript
import type { Category } from '~/entities/category/model/category.schema'

interface BreadcrumbItem {
  label: string
  to?: string
}

export function useProductBreadcrumbs(
  partCategoryChain: Ref<Category[]> | ComputedRef<Category[]>,
  productTitle: Ref<string> | ComputedRef<string>,
) {
  const { t } = useI18n()

  const breadcrumbs = computed<BreadcrumbItem[]>(() => {
    const items: BreadcrumbItem[] = [
      { label: t('breadcrumbs.home'), to: '/' },
    ]

    for (const cat of partCategoryChain.value) {
      items.push({
        label: cat.name,
        to: `/catalog?category_id=${cat.id}`,
      })
    }

    // Last item — product title, no link
    items.push({ label: productTitle.value })

    return items
  })

  return { breadcrumbs }
}

Step 2: Commit

bash
git add app/features/product-breadcrumbs/composables/useProductBreadcrumbs.ts
git commit -m "feat(DEV-200): add useProductBreadcrumbs composable"

Task 5: Add breadcrumbs to product page + i18n key

Files:

  • Modify: i18n/locales/ru.json
  • Modify: app/pages/product/[id].vue

Step 1: Add breadcrumbs i18n key

In ru.json, add after the "common" section (or inside it):

json
"breadcrumbs": {
  "home": "Главная"
}

Step 2: Add breadcrumbs to product page template

In app/pages/product/[id].vue, in the <script setup> section, after the partCategoryChain computed (around line 64), add:

typescript
const productTitle = computed(() => product.value?.title ?? '')
const { breadcrumbs } = useProductBreadcrumbs(partCategoryChain, productTitle)

In the <template>, inside the v-else-if="product" div (line 151), add breadcrumbs before the flex container (before line 152 <div class="flex flex-col lg:flex-row gap-8">):

vue
<UBreadcrumb :items="breadcrumbs" class="mb-4" />

Step 3: Verify in browser

  • Open any product page, e.g. http://localhost:3000/product/1
  • Should see: Главная › Кузов › Оптика › Фара левая BMW E46
  • "Главная" and category links should be clickable
  • Product title (last item) should be muted text, not clickable

Step 4: Commit

bash
git add i18n/locales/ru.json app/pages/product/[id].vue app/features/product-breadcrumbs/composables/useProductBreadcrumbs.ts
git commit -m "feat(DEV-200): add breadcrumbs to product page"

Task 6: Compact category grid on homepage

Files:

  • Modify: app/pages/index.vue

Step 1: Update the categories grid

In app/pages/index.vue, find the categories grid section (around line 179). Make these changes:

  1. Change grid minmax(160px, 1fr)minmax(120px, 1fr)
  2. Change icon size h-8 w-8h-6 w-6
  3. Change text size text-smtext-xs

Before (line 179):

html
<div class="grid gap-3" style="grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));">

After:

html
<div class="grid gap-3" style="grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));">

Before (line 186):

html
<UIcon :name="getCategoryIcon(cat.icon)" class="h-8 w-8 text-[var(--ui-text-muted)]" />

After:

html
<UIcon :name="getCategoryIcon(cat.icon)" class="h-6 w-6 text-[var(--ui-text-muted)]" />

Before (line 187):

html
<span class="text-center text-sm font-medium">{{ cat.name }}</span>

After:

html
<span class="text-center text-xs font-medium">{{ cat.name }}</span>

Step 2: Verify in browser

  • Open http://localhost:3000
  • Categories grid should be more compact — more items per row
  • Icons smaller, text smaller — matching wireframes density

Step 3: Commit

bash
git add app/pages/index.vue
git commit -m "feat(DEV-200): compact category grid to match wireframes"

Task 7: Hero divider text styling

Files:

  • Modify: app/pages/index.vue

Step 1: Add uppercase + tracking to divider text

In app/pages/index.vue, find the divider text (around line 113):

Before:

html
<span class="text-sm text-[var(--ui-text-muted)]">{{ t('home.hero.orByVehicle') }}</span>

After:

html
<span class="text-sm text-[var(--ui-text-muted)] uppercase tracking-wider">{{ t('home.hero.orByVehicle') }}</span>

Step 2: Verify in browser

  • Open http://localhost:3000
  • The "или подберите по автомобилю" text between the lines should now be UPPERCASE with wider letter spacing

Step 3: Commit

bash
git add app/pages/index.vue
git commit -m "style(DEV-200): uppercase hero divider text per wireframes"

Files:

  • Delete: app/widgets/footer/.gitkeep (if exists, now that we have a real file)

Step 1: Remove .gitkeep

bash
rm -f app/widgets/footer/.gitkeep

Step 2: Commit

bash
git add -A app/widgets/footer/
git commit -m "chore(DEV-200): remove footer .gitkeep"

Final Verification

After all tasks, verify:

bash
npm run lint
npm run typecheck

Both must pass with no new errors.