Design language introduced here and reused across both screens:
- LESS colour tokens (@ink/@inkSoft/@inkMuted/@inkFaint, @wine/@wineSoft,
@canvas/@canvasSoft, @hairline/@hairlineSoft) added to
themeConfig/colors.json and injected globally via vite's modifyVars, so
scoped <style lang="less"> blocks resolve them with no import
- BEM-ish scoped class names, hairline-bordered cards at 14-18px radii,
ink surfaces with a wine accent, dashed-border empty states
- components/home/: SectionHead, ShowcaseCard, ShowcaseSlider, CategoryRail,
RateStrip, AccountStrip
Data honesty is deliberate throughout: no rial price is printed for a piece,
because a finished item's price is the live gold rate x weight plus wage,
computed at order time and never stored on the product. Only genuinely
present fields are shown (weight, making-charge percent, stock, category,
brand). Categories and the hero are derived from the real catalogue, so an
empty catalogue renders an empty state rather than placeholder tiles.
Also fixes a real bug in the product detail view: it ran, at module scope,
history.pushState + window.onpopstate = () => history.go(1). Because the
drawer mounts its slot eagerly, that fired for every product card on the
home screen and permanently hijacked the back button for the session.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
60 lines
1.2 KiB
Vue
60 lines
1.2 KiB
Vue
<!-- Section heading with an optional "see all" affordance. -->
|
|
<template>
|
|
<header class="sec">
|
|
<div class="sec__text">
|
|
<h2 class="sec__title">{{ title }}</h2>
|
|
<p v-if="subtitle" class="sec__sub">{{ subtitle }}</p>
|
|
</div>
|
|
<button v-if="actionLabel" type="button" class="sec__action" @click="emit('action')">
|
|
{{ actionLabel }}
|
|
<Icon icon="vuesax-linear:arrow-left-2" size="15" />
|
|
</button>
|
|
</header>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import Icon from '../icon/Icon.vue'
|
|
|
|
defineProps<{ title: string; subtitle?: string; actionLabel?: string }>()
|
|
const emit = defineEmits<{ (e: 'action'): void }>()
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.sec {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
padding-bottom: 13px;
|
|
}
|
|
|
|
.sec__title {
|
|
margin: 0;
|
|
font-size: 15px;
|
|
font-weight: 800;
|
|
color: @ink;
|
|
letter-spacing: -0.1px;
|
|
}
|
|
|
|
.sec__sub {
|
|
margin: 5px 0 0;
|
|
font-size: 11.5px;
|
|
color: @inkFaint;
|
|
}
|
|
|
|
.sec__action {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
border: 0;
|
|
background: none;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
font-family: inherit;
|
|
font-size: 11.5px;
|
|
font-weight: 700;
|
|
color: @wine;
|
|
white-space: nowrap;
|
|
}
|
|
</style>
|