From 61e3e9f916ce0d2f238ce8608d584b1d173db623 Mon Sep 17 00:00:00 2001 From: Oscar Date: Wed, 3 Jun 2026 20:41:11 +0300 Subject: [PATCH] upd --- frontend/.claude/skills/taste-skill/SKILL.md | 85 ++++ frontend/app/app.vue | 62 ++- frontend/app/assets/styles/main.scss | 31 +- frontend/app/components/LeftPanel.vue | 433 +++++++++++++++---- frontend/app/components/RightPanel.vue | 277 +++++++++--- 5 files changed, 716 insertions(+), 172 deletions(-) create mode 100644 frontend/.claude/skills/taste-skill/SKILL.md diff --git a/frontend/.claude/skills/taste-skill/SKILL.md b/frontend/.claude/skills/taste-skill/SKILL.md new file mode 100644 index 0000000..44ead27 --- /dev/null +++ b/frontend/.claude/skills/taste-skill/SKILL.md @@ -0,0 +1,85 @@ +--- +name: minimalist-ui +description: Clean editorial-style interfaces. Warm monochrome palette, typographic contrast, flat bento grids, muted pastels. No gradients, no heavy shadows. +--- + +# Protocol: Premium Utilitarian Minimalism UI Architect + +## 1. Protocol Overview +Name: Premium Utilitarian Minimalism & Editorial UI +Description: An advanced frontend engineering directive for generating highly refined, ultra-minimalist, "document-style" web interfaces analogous to top-tier workspace platforms. This protocol strictly enforces a high-contrast warm monochrome palette, bespoke typographic hierarchies, meticulous structural macro-whitespace, bento-grid layouts, and an ultra-flat component architecture with deliberate muted pastel accents. It actively rejects standard generic SaaS design trends. + +## 2. Absolute Negative Constraints (Banned Elements) +The AI must strictly avoid the following generic web development defaults: +- DO NOT use the "Inter", "Roboto", or "Open Sans" typefaces. +- DO NOT use generic, thin-line icon libraries like "Lucide", "Feather", or standard "Heroicons". +- DO NOT use Tailwind's default heavy drop shadows (e.g., `shadow-md`, `shadow-lg`, `shadow-xl`). Shadows must be practically non-existent or heavily customized to be ultra-diffuse and low opacity (< 0.05). +- DO NOT use primary colored backgrounds for large elements or sections (e.g., no bright blue, green, or red hero sections). +- DO NOT use gradients, neon colors, or 3D glassmorphism (beyond subtle navbar blurs). +- DO NOT use `rounded-full` (pill shapes) for large containers, cards, or primary buttons. +- DO NOT use emojis anywhere in code, markup, text content, headings, or alt text. Replace with proper icons or clean SVG primitives. +- DO NOT use generic placeholder names like "John Doe", "Acme Corp", or "Lorem Ipsum". Use realistic, contextual content. +- DO NOT use AI copywriting clichés: "Elevate", "Seamless", "Unleash", "Next-Gen", "Game-changer", "Delve". Write plain, specific language. + +## 3. Typographic Architecture +The interface must rely on extreme typographic contrast and premium font selection to establish an editorial feel. +- Primary Sans-Serif (Body, UI, Buttons): Use clean, geometric, or system-native fonts with character. Target: `font-family: 'SF Pro Display', 'Geist Sans', 'Helvetica Neue', 'Switzer', sans-serif`. +- Editorial Serif (Hero Headings & Quotes): Target: `font-family: 'Lyon Text', 'Newsreader', 'Playfair Display', 'Instrument Serif', serif`. Apply tight tracking (`letter-spacing: -0.02em` to `-0.04em`) and tight line-height (`1.1`). +- Monospace (Code, Keystrokes, Meta-data): Target: `font-family: 'Geist Mono', 'SF Mono', 'JetBrains Mono', monospace`. +- Text Colors: Body text must never be absolute black (`#000000`). Use off-black/charcoal (`#111111` or `#2F3437`) with a generous `line-height` of `1.6` for legibility. Secondary text should be muted gray (`#787774`). + +## 4. Color Palette (Warm Monochrome + Spot Pastels) +Color is a scarce resource, utilized only for semantic meaning or subtle accents. +- Canvas / Background: Pure White `#FFFFFF` or Warm Bone/Off-White `#F7F6F3` / `#FBFBFA`. +- Primary Surface (Cards): `#FFFFFF` or `#F9F9F8`. +- Structural Borders / Dividers: Ultra-light gray `#EAEAEA` or `rgba(0,0,0,0.06)`. +- Accent Colors: Exclusively use highly desaturated, washed-out pastels for tags, inline code backgrounds, or subtle icon backgrounds. + - Pale Red: `#FDEBEC` (Text: `#9F2F2D`) + - Pale Blue: `#E1F3FE` (Text: `#1F6C9F`) + - Pale Green: `#EDF3EC` (Text: `#346538`) + - Pale Yellow: `#FBF3DB` (Text: `#956400`) + +## 5. Component Specifications +- Bento Box Feature Grids: + - Utilize asymmetrical CSS Grid layouts. + - Cards must have exactly `border: 1px solid #EAEAEA`. + - Border-radius must be crisp: `8px` or `12px` maximum. + - Internal padding must be generous (e.g., `24px` to `40px`). +- Primary Call-To-Action (Buttons): + - Solid background `#111111`, text `#FFFFFF`. + - Slight border-radius (`4px` to `6px`). No box-shadow. + - Hover state should be a subtle color shift to `#333333` or a micro-scale `transform: scale(0.98)`. +- Tags & Status Badges: + - Pill-shaped (`border-radius: 9999px`), very small typography (`text-xs`), uppercase with wide tracking (`letter-spacing: 0.05em`). + - Background must use the defined Muted Pastels. +- Accordions (FAQ): + - Strip all container boxes. Separate items only with a `border-bottom: 1px solid #EAEAEA`. + - Use a clean, sharp `+` and `-` icon for the toggle state. +- Keystroke Micro-UIs: + - Render shortcuts as physical keys using `` tags: `border: 1px solid #EAEAEA`, `border-radius: 4px`, `background: #F7F6F3`, using the Monospace font. +- Faux-OS Window Chrome: + - When mocking up software, wrap it in a minimalist container with a white top bar containing three small, light gray circles (replicating macOS window controls). + +## 6. Iconography & Imagery Directives +- System Icons: Use "Phosphor Icons (Bold or Fill weights)" or "Radix UI Icons" for a technical, slightly thicker-stroke aesthetic. Standardize stroke width across all icons. +- Illustrations: Monochromatic, rough continuous-line ink sketches on a white background, featuring a single offset geometric shape filled with a muted pastel color. +- Photography: Use high-quality, desaturated images with a warm tone. Apply subtle overlays (`opacity: 0.04` warm grain) to blend photos into the monochrome palette. Never use oversaturated stock photos. Use reliable placeholders like `https://picsum.photos/seed/{context}/1200/800` when real assets are unavailable. +- Hero & Section Backgrounds: Sections should not feel empty and flat. Use subtle full-width background imagery at very low opacity, soft radial light spots (`radial-gradient` with warm tones at `opacity: 0.03`), or minimal geometric line patterns to add depth without breaking the clean aesthetic. + +## 7. Subtle Motion & Micro-Animations +Motion should feel invisible — present but never distracting. The goal is quiet sophistication, not spectacle. +- Scroll Entry: Elements fade in gently as they enter the viewport. Use `translateY(12px)` + `opacity: 0` resolving over `600ms` with `cubic-bezier(0.16, 1, 0.3, 1)`. Use `IntersectionObserver`, never `window.addEventListener('scroll')`. +- Hover States: Cards lift with an ultra-subtle shadow shift (`box-shadow` transitioning from `0 0 0` to `0 2px 8px rgba(0,0,0,0.04)` over `200ms`). Buttons respond with `scale(0.98)` on `:active`. +- Staggered Reveals: Lists and grid items enter with a cascade delay (`animation-delay: calc(var(--index) * 80ms)`). Never mount everything at once. +- Background Ambient Motion: Optional. A single, very slow-moving radial gradient blob (`animation-duration: 20s+`, `opacity: 0.02-0.04`) drifting behind hero sections. Must be applied to a `position: fixed; pointer-events: none` layer. Never on scrolling containers. +- Performance: Animate exclusively via `transform` and `opacity`. No layout-triggering properties (`top`, `left`, `width`, `height`). Use `will-change: transform` sparingly and only on actively animating elements. + +## 8. Execution Protocol +When tasked with writing frontend code (HTML, React, Tailwind, Vue) or designing a layout: +1. Establish the macro-whitespace first. Use massive vertical padding between sections (e.g., `py-24` or `py-32` in Tailwind). +2. Constrain the main typography content width to `max-w-4xl` or `max-w-5xl`. +3. Apply the custom typographic hierarchy and monochromatic color variables immediately. +4. Ensure every card, divider, and border adheres strictly to the `1px solid #EAEAEA` rule. +5. Add scroll-entry animations to all major content blocks. +6. Ensure sections have visual depth through imagery, ambient gradients, or subtle textures — no empty flat backgrounds. +7. Provide code that reflects this high-end, uncluttered, editorial aesthetic natively without requiring manual adjustments. diff --git a/frontend/app/app.vue b/frontend/app/app.vue index 8cae4b6..a3a689c 100644 --- a/frontend/app/app.vue +++ b/frontend/app/app.vue @@ -1,7 +1,13 @@ @@ -110,6 +136,11 @@ function onSearchInput(e: Event) { }, 300) } +function closeModal() { + showAddModal.value = false + addIdInput.value = null +} + function submitAdd() { if (addIdInput.value !== null && Number.isInteger(addIdInput.value)) { emit('add', addIdInput.value) @@ -124,72 +155,125 @@ function submitAdd() { display: flex; flex-direction: column; height: 100%; - border-right: 1px solid #e5e7eb; + background: var(--surface); + border-right: 1px solid var(--border); } +/* ── Header ── */ .panel-header { - padding: 16px; - border-bottom: 1px solid #e5e7eb; - background: #f9fafb; - - h2 { - margin: 0 0 12px; - font-size: 1.1rem; - font-weight: 600; - } + padding: 18px 20px 16px; + border-bottom: 1px solid var(--border); + background: var(--surface); + flex-shrink: 0; } -.panel-controls { +.panel-title-row { display: flex; - gap: 8px; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; +} + +.panel-title { + margin: 0; + font-size: 0.72rem; + font-weight: 500; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--text-secondary); + font-family: var(--font-sans); +} + +.search-wrap { + position: relative; + display: flex; + align-items: center; +} + +.search-icon { + position: absolute; + left: 10px; + display: flex; + align-items: center; + color: var(--text-muted); + pointer-events: none; } .search-input { - flex: 1; - padding: 6px 10px; - border: 1px solid #d1d5db; - border-radius: 6px; - font-size: 0.9rem; + width: 100%; + padding: 7px 12px 7px 30px; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + background: var(--surface-subtle); + font-family: var(--font-sans); + font-size: 0.825rem; + color: var(--text-primary); outline: none; + transition: border-color 150ms ease, background 150ms ease; + + &::placeholder { + color: var(--text-muted); + } &:focus { - border-color: #6366f1; - box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.15); + border-color: var(--text-primary); + background: var(--surface); } } -.btn { - padding: 6px 14px; - border: 1px solid #d1d5db; - border-radius: 6px; - background: #fff; +/* ── Buttons ── */ +.btn-primary { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + background: var(--text-primary); + color: #FFFFFF; + border: none; + border-radius: var(--radius-sm); + font-family: var(--font-sans); + font-size: 0.78rem; + font-weight: 500; cursor: pointer; - font-size: 0.9rem; + letter-spacing: 0.01em; white-space: nowrap; + transition: background 150ms ease, transform 100ms ease; &:hover { - background: #f3f4f6; + background: #333333; } - &.btn-primary { - background: #6366f1; - color: #fff; - border-color: #6366f1; - - &:hover { - background: #4f46e5; - } - } - - &.btn-icon { - padding: 4px 10px; - font-size: 1rem; + &:active { + transform: scale(0.97); } } +.btn-ghost { + padding: 7px 14px; + background: none; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + font-family: var(--font-sans); + font-size: 0.825rem; + color: var(--text-secondary); + cursor: pointer; + transition: color 150ms ease, border-color 150ms ease, background 150ms ease; + + &:hover { + color: var(--text-primary); + border-color: #999; + background: var(--surface-subtle); + } +} + +/* ── Body & list ── */ .panel-body { flex: 1; overflow-y: auto; + + &::-webkit-scrollbar { width: 3px; } + &::-webkit-scrollbar-track { background: transparent; } + &::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; } } .item-list { @@ -198,63 +282,220 @@ function submitAdd() { padding: 0; } +@keyframes item-in { + from { opacity: 0; transform: translateY(6px); } + to { opacity: 1; transform: translateY(0); } +} + .item-row { display: flex; align-items: center; justify-content: space-between; - padding: 8px 16px; - border-bottom: 1px solid #f3f4f6; + padding: 9px 12px 9px 20px; + border-bottom: 1px solid var(--border); + animation: item-in 350ms cubic-bezier(0.16, 1, 0.3, 1) both; + animation-delay: calc(var(--i, 0) * 25ms); + transition: background 120ms ease; &:hover { - background: #f9fafb; + background: var(--surface-subtle); + + .action-btn { opacity: 1; } } } -.item-label { - font-size: 0.9rem; - color: #374151; +.item-id { + font-family: var(--font-mono); + font-size: 0.8rem; + color: var(--text-primary); + + &::before { + content: '#'; + color: var(--text-muted); + margin-right: 1px; + } } -.loading-text, -.empty-text { - padding: 16px; - text-align: center; - color: #9ca3af; - font-size: 0.9rem; +.action-btn { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + background: none; + border: none; + border-radius: var(--radius-sm); + color: var(--text-secondary); + cursor: pointer; + opacity: 0; + transition: color 120ms ease, background 120ms ease, opacity 120ms ease; + + &:hover { + color: var(--text-primary); + background: var(--border); + } +} + +/* ── States ── */ +.status-row { + display: flex; + justify-content: center; + padding: 24px; +} + +.status-dots { + display: flex; + gap: 5px; + align-items: center; + + span { + width: 4px; + height: 4px; + border-radius: 50%; + background: var(--text-muted); + animation: dot-blink 1.2s ease-in-out infinite; + + &:nth-child(2) { animation-delay: 0.2s; } + &:nth-child(3) { animation-delay: 0.4s; } + } +} + +@keyframes dot-blink { + 0%, 80%, 100% { opacity: 0.25; } + 40% { opacity: 1; } +} + +.empty-state { + display: flex; + align-items: center; + justify-content: center; + padding: 56px 24px; +} + +.empty-label { + font-size: 0.8rem; + color: var(--text-muted); + letter-spacing: 0.02em; } .sentinel { height: 1px; } +/* ── Modal ── */ .modal-overlay { position: fixed; inset: 0; - background: rgba(0, 0, 0, 0.4); + background: rgba(0, 0, 0, 0.24); + backdrop-filter: blur(2px); display: flex; align-items: center; justify-content: center; - z-index: 100; + z-index: 200; } -.modal { - background: #fff; - border-radius: 10px; - padding: 24px; - min-width: 320px; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); +.modal-card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-md); + padding: 28px; + min-width: 340px; + box-shadow: 0 4px 32px rgba(0, 0, 0, 0.06); +} - h3 { - margin: 0 0 16px; - font-size: 1rem; - font-weight: 600; +.modal-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 20px; +} + +.modal-title { + margin: 0; + font-size: 0.88rem; + font-weight: 500; + color: var(--text-primary); + letter-spacing: -0.01em; +} + +.modal-close { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + background: none; + border: none; + border-radius: var(--radius-sm); + color: var(--text-secondary); + cursor: pointer; + transition: color 120ms ease, background 120ms ease; + + &:hover { + color: var(--text-primary); + background: var(--surface-subtle); } } -.modal-actions { +.modal-input { + width: 100%; + padding: 9px 12px; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + background: var(--surface-subtle); + font-family: var(--font-mono); + font-size: 0.875rem; + color: var(--text-primary); + outline: none; + transition: border-color 150ms ease, background 150ms ease; + + &::placeholder { + color: var(--text-muted); + font-family: var(--font-sans); + } + + &:focus { + border-color: var(--text-primary); + background: var(--surface); + } + + &::-webkit-inner-spin-button, + &::-webkit-outer-spin-button { + -webkit-appearance: none; + } +} + +.modal-footer { display: flex; gap: 8px; justify-content: flex-end; - margin-top: 16px; + margin-top: 20px; +} + +/* Modal enter/leave transition */ +.modal-enter-active { + transition: opacity 180ms ease; + + .modal-card { + transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1), opacity 180ms ease; + } +} + +.modal-leave-active { + transition: opacity 150ms ease; + + .modal-card { + transition: transform 150ms ease, opacity 150ms ease; + } +} + +.modal-enter-from, +.modal-leave-to { + opacity: 0; + + .modal-card { + transform: translateY(10px) scale(0.98); + opacity: 0; + } } diff --git a/frontend/app/components/RightPanel.vue b/frontend/app/components/RightPanel.vue index 59e9dbf..5668a17 100644 --- a/frontend/app/components/RightPanel.vue +++ b/frontend/app/components/RightPanel.vue @@ -1,8 +1,17 @@