Appearance
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
Task 1: Add footer i18n keys
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:
- Change grid
minmax(160px, 1fr)→minmax(120px, 1fr) - Change icon size
h-8 w-8→h-6 w-6 - Change text size
text-sm→text-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"Task 8: Delete .gitkeep from footer widget directory
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/.gitkeepStep 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 typecheckBoth must pass with no new errors.