refactor: update layout and styling for overlay and sheet components
This commit is contained in:
+3
-3
@@ -47,7 +47,7 @@ export default function App() {
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="mx-auto flex min-h-dvh w-full max-w-105 flex-col px-4 pt-5">
|
||||
<div class="pt-screen-safe mx-auto flex min-h-dvh w-full max-w-105 flex-col px-4">
|
||||
{!ready.value && (
|
||||
<div class="flex flex-1 items-center justify-center">
|
||||
<span class="text-display animate-pulse text-2xl font-light text-ember-bright">Ккал</span>
|
||||
@@ -62,7 +62,7 @@ export default function App() {
|
||||
|
||||
{ready.value && hasProfile.value && (
|
||||
<>
|
||||
<main class="flex-1 pb-30">
|
||||
<main class="pb-nav-safe flex-1">
|
||||
{activeTab.value === 'diary' && <DiaryScreen />}
|
||||
{activeTab.value === 'stats' && <StatsScreen />}
|
||||
{activeTab.value === 'foods' && <FoodsScreen />}
|
||||
@@ -71,7 +71,7 @@ export default function App() {
|
||||
|
||||
{/* Нижняя навигация */}
|
||||
<nav class="fixed inset-x-0 bottom-0 z-40 flex justify-center">
|
||||
<div class="flex w-full max-w-105 items-end border-t hairline bg-[#151210]/92 px-2 pt-2 pb-[max(env(safe-area-inset-bottom),10px)] backdrop-blur-md">
|
||||
<div class="pb-bar-safe flex w-full max-w-105 items-end border-t hairline bg-[#151210]/92 px-2 pt-2 backdrop-blur-md">
|
||||
{TABS.slice(0, 2).map(tab => (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
+104
-62
@@ -25,29 +25,40 @@
|
||||
--color-over-bright: #e8836f;
|
||||
}
|
||||
|
||||
html {
|
||||
background: var(--color-bg);
|
||||
color-scheme: dark;
|
||||
}
|
||||
@layer base {
|
||||
html {
|
||||
@apply bg-bg;
|
||||
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
color: var(--color-ink);
|
||||
background:
|
||||
radial-gradient(120% 70% at 50% 0%, #201b14 0%, var(--color-bg) 55%),
|
||||
var(--color-bg);
|
||||
min-height: 100dvh;
|
||||
}
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* Зерно поверх фона — атмосфера бумаги при свете лампы. */
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 70;
|
||||
opacity: 0.05;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
|
||||
body {
|
||||
@apply min-h-dvh font-sans text-ink;
|
||||
|
||||
background:
|
||||
radial-gradient(120% 70% at 50% 0%, #201b14 0%, var(--color-bg) 55%),
|
||||
var(--color-bg);
|
||||
}
|
||||
|
||||
/* Зерно поверх фона — атмосфера бумаги при свете лампы. */
|
||||
body::after {
|
||||
@apply pointer-events-none fixed inset-0 z-70 opacity-5;
|
||||
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* Прячем спиннеры числовых инпутов — порции вводятся с клавиатуры и чипсами. */
|
||||
input[type='number']::-webkit-outer-spin-button,
|
||||
input[type='number']::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
}
|
||||
|
||||
@utility text-display {
|
||||
@@ -71,15 +82,39 @@ body::after {
|
||||
}
|
||||
}
|
||||
|
||||
/* Появление контента: мягкий подъём, каскад через animation-delay. */
|
||||
@keyframes rise {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
/*
|
||||
* Каскадные слои на @keyframes не влияют (у анимаций своя область имён,
|
||||
* побеждает последнее объявление) — держим их в base только ради единообразия.
|
||||
*/
|
||||
@layer base {
|
||||
/* Появление контента: мягкий подъём, каскад через animation-delay. */
|
||||
@keyframes rise {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
|
||||
@keyframes sheet-up {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,48 +122,55 @@ body::after {
|
||||
animation: rise 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
}
|
||||
|
||||
@keyframes sheet-up {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@utility animate-sheet-up {
|
||||
animation: sheet-up 0.38s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@utility animate-fade-in {
|
||||
animation: fade-in 0.25s ease-out both;
|
||||
}
|
||||
|
||||
/* Кольцо калорий: плавное перетекание дуги при изменении данных. */
|
||||
.ring-arc {
|
||||
transition: stroke-dashoffset 0.7s cubic-bezier(0.22, 1, 0.36, 1), stroke 0.4s ease;
|
||||
/*
|
||||
* Safe-area. В index.html стоит viewport-fit=cover, поэтому контент уходит под
|
||||
* статус-бар и home indicator — края экрана обязаны добавлять инсеты к своим
|
||||
* отступам. В портрете (манифест фиксирует orientation) боковые инсеты нулевые,
|
||||
* поэтому здесь только верх и низ.
|
||||
*/
|
||||
@layer utilities {
|
||||
/* Верх экрана: собственный отступ плюс статус-бар/вырез. */
|
||||
.pt-screen-safe {
|
||||
padding-top: calc(--spacing(5) + env(safe-area-inset-top));
|
||||
}
|
||||
|
||||
/* Нижняя панель: отступ не меньше высоты home indicator. */
|
||||
.pb-bar-safe {
|
||||
padding-bottom: max(env(safe-area-inset-bottom), --spacing(2.5));
|
||||
}
|
||||
|
||||
/* Скролл контента заканчивается над панелью навигации, а не под ней. */
|
||||
.pb-nav-safe {
|
||||
padding-bottom: calc(--spacing(30) + env(safe-area-inset-bottom));
|
||||
}
|
||||
}
|
||||
|
||||
.bar-fill {
|
||||
transition: width 0.5s cubic-bezier(0.22, 1, 0.36, 1), background-color 0.3s ease;
|
||||
}
|
||||
@layer components {
|
||||
/* Кольцо калорий: плавное перетекание дуги при изменении данных. */
|
||||
.ring-arc {
|
||||
transition: stroke-dashoffset 0.7s cubic-bezier(0.22, 1, 0.36, 1), stroke 0.4s ease;
|
||||
}
|
||||
|
||||
/* Прячем спиннеры числовых инпутов — порции вводятся с клавиатуры и чипсами. */
|
||||
input[type='number']::-webkit-outer-spin-button,
|
||||
input[type='number']::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
input[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
.bar-fill {
|
||||
transition: width 0.5s cubic-bezier(0.22, 1, 0.36, 1), background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.sheet-overlay {
|
||||
@apply animate-fade-in absolute inset-0 bg-black/65 backdrop-blur-[2px];
|
||||
}
|
||||
|
||||
/* Лист прижат к низу: фон доходит до края экрана, контент — нет. */
|
||||
.sheet-panel {
|
||||
@apply animate-sheet-up relative flex w-full max-w-105 flex-col rounded-t-3xl border border-b-0 hairline bg-[#191511] shadow-[0_-24px_80px_rgba(0,0,0,0.5)];
|
||||
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,9 +205,9 @@ export default function AddSheet() {
|
||||
|
||||
return (
|
||||
<div class="fixed inset-0 z-50 flex items-end justify-center">
|
||||
<div class="animate-fade-in absolute inset-0 bg-black/65 backdrop-blur-[2px]" onClick={close} />
|
||||
<div class="sheet-overlay" onClick={close} />
|
||||
|
||||
<div class="animate-sheet-up relative flex max-h-[90dvh] w-full max-w-105 flex-col rounded-t-3xl border border-b-0 hairline bg-[#191511] shadow-[0_-24px_80px_rgba(0,0,0,0.5)]">
|
||||
<div class="sheet-panel max-h-[90dvh]">
|
||||
{/* Шапка */}
|
||||
<div class="flex items-center gap-2 px-5 pt-4 pb-3">
|
||||
{step.value !== 'pick' && (
|
||||
|
||||
@@ -82,9 +82,9 @@ export default function EditEntrySheet() {
|
||||
|
||||
return (
|
||||
<div class="fixed inset-0 z-50 flex items-end justify-center">
|
||||
<div class="animate-fade-in absolute inset-0 bg-black/65 backdrop-blur-[2px]" onClick={close} />
|
||||
<div class="sheet-overlay" onClick={close} />
|
||||
|
||||
<div class="animate-sheet-up relative flex w-full max-w-105 flex-col rounded-t-3xl border border-b-0 hairline bg-[#191511] shadow-[0_-24px_80px_rgba(0,0,0,0.5)]">
|
||||
<div class="sheet-panel">
|
||||
<div class="flex items-center gap-2 px-5 pt-4 pb-3">
|
||||
<h2 class="text-display min-w-0 flex-1 truncate text-lg font-medium">{entry.value?.name ?? ''}</h2>
|
||||
<button
|
||||
|
||||
@@ -68,9 +68,9 @@ export default function FoodFormSheet() {
|
||||
|
||||
return (
|
||||
<div class="fixed inset-0 z-50 flex items-end justify-center">
|
||||
<div class="animate-fade-in absolute inset-0 bg-black/65 backdrop-blur-[2px]" onClick={close} />
|
||||
<div class="sheet-overlay" onClick={close} />
|
||||
|
||||
<div class="animate-sheet-up relative flex max-h-[90dvh] w-full max-w-105 flex-col rounded-t-3xl border border-b-0 hairline bg-[#191511] shadow-[0_-24px_80px_rgba(0,0,0,0.5)]">
|
||||
<div class="sheet-panel max-h-[90dvh]">
|
||||
<div class="flex items-center gap-2 px-5 pt-4 pb-3">
|
||||
<h2 class="text-display flex-1 text-lg font-medium">{existing.value ? 'Продукт' : 'Новый продукт'}</h2>
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user