Applies to both user-app and user-app-v2. Logic and structure are shared; each app keeps its own look. Appearance settings - New "تنظیمات ظاهری" page (/settings/appearance), opened from the dashboard and available without login. It offers a light/dark/system theme and a "fast & light" motion switch. - appearanceStore saves the choice to localStorage. A small inline boot script in index.html applies it before first paint so there is no white flash. The "system" option follows OS changes live. - New ToggleSwitch component. Dark mode - Theme LESS tokens now compile to CSS custom properties. Light/dark values come from a generated virtual:palette.css (themeConfig/ appearance.ts, with colors.json + new colors.dark.json). - New LESS alpha() plugin replaces fade() on theme tokens. - New semantic tokens: page, onInk, heroSurface, hairlineStrong, photoMat, photoStage. - The dark palette is layered (page < card < panel < border) so surfaces separate without shadows. - Hard-coded colors were replaced by tokens according to their role. - antd inputs/text get dark overrides; vant uses its own .van-theme-dark. - Light mode looks the same as before. Reduced motion - With html.motion-reduced, transitions are capped at 120ms, keyframe loops stop, and backdrop blurs and permanent will-change are dropped. - Spinners and the toast progress bar (which closes the toast) are exempt via .keep-motion. - Carousel autoplay, the v1 Lottie and the product morph honor the setting. Product detail - Opening a product is now a container-transform morph from the tapped card (ProductMorph). It replaces the bottom Drawer and uses composited transform/opacity only. - The detail view is seeded with the card's data, so it no longer opens blank and jumps. Filters and sheets - Filters are now a full-width bottom sheet. Selections stay a draft until applied, so the result list no longer re-renders while dragging the slider. - DropDown collapse animates grid-template-rows instead of a capped max-height. - v2 IslandSheet is restyled to the v2 design language (API unchanged). Fixes - filtersStore.reset() shallow-copied shared arrays, so clearing the filters never cleared the selected brands.
60 lines
1.7 KiB
Vue
60 lines
1.7 KiB
Vue
<!--
|
|
The visual half of an on/off switch: a track and a knob. Deliberately not a
|
|
control on its own — the row that holds it is the <button role="switch">,
|
|
so the whole row is one tap target and screen readers announce one switch
|
|
(see views/user/AppearanceView.vue).
|
|
|
|
STRUCTURE IS SHARED, LOOK IS NOT: identical markup and script in user-app
|
|
and user-app-v2; only the "Look" block differs, per app.
|
|
-->
|
|
<template>
|
|
<span class="tsw" :class="{ 'is-on': on }" aria-hidden="true">
|
|
<span class="tsw__knob"></span>
|
|
</span>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
defineProps<{ on: boolean }>()
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
/* ── Mechanics (same in both apps) ──────────────────────────── */
|
|
.tsw {
|
|
position: relative;
|
|
flex: 0 0 auto;
|
|
display: inline-block;
|
|
width: 46px;
|
|
height: 28px;
|
|
border-radius: 999px;
|
|
transition: background-color 0.22s ease;
|
|
}
|
|
|
|
.tsw__knob {
|
|
position: absolute;
|
|
top: 3px;
|
|
// RTL page: "off" sits on the right (start), "on" slides to the left (end).
|
|
inset-inline-start: 3px;
|
|
width: 22px;
|
|
height: 22px;
|
|
border-radius: 50%;
|
|
box-shadow: 0 2px 6px -1px rgba(20, 22, 26, 0.35);
|
|
transition: transform 0.26s cubic-bezier(0.32, 0.72, 0, 1), background-color 0.22s ease;
|
|
}
|
|
|
|
.tsw.is-on .tsw__knob {
|
|
transform: translateX(-18px);
|
|
}
|
|
|
|
/* ── Look: user-app ─────────────────────────────────────────── */
|
|
// The app's chosen state is brand green (@primary, as on chips and CTAs).
|
|
.tsw {
|
|
background: @lightGray1;
|
|
}
|
|
.tsw__knob {
|
|
background: #fff;
|
|
}
|
|
.tsw.is-on {
|
|
background: @primary;
|
|
}
|
|
</style>
|