From 55c90e8c602cdf537c57a3548a3295ca50f8dc94 Mon Sep 17 00:00:00 2001 From: Mplan Date: Sat, 25 Jul 2026 02:41:27 +0800 Subject: [PATCH 1/3] style(ui): unify sky-atlas visual system Centralize interactive component styles, adopt the fresh eucalyptus palette, and keep map controls square with hard shadows. Update project guidance and the OpenCloud style skill to match the implementation. --- .agents/skills/opencloud-style/SKILL.md | 483 +++++++----------- AGENTS.md | 38 +- docs/style-system.md | 17 +- scripts/check-style-system.mjs | 25 +- src/components/cloud/CloudEditModal.vue | 8 +- src/components/cloud/ImageDetailModal.vue | 6 +- src/components/cloud/MapPickerModal.vue | 4 +- src/components/cloud/MiniLocationMap.vue | 2 +- src/components/cloud/QuickUploadModal.vue | 8 +- src/components/layout/AppHeader.vue | 44 +- .../profile/ContributionHeatmap.vue | 2 +- src/lib/theme.ts | 13 +- src/styles/components.css | 346 +++++++++++-- src/styles/tokens.css | 45 +- src/views/admin/AdminView.vue | 24 +- src/views/auth/ForgotPasswordView.vue | 8 +- src/views/auth/LoginView.vue | 14 +- src/views/auth/RegisterView.vue | 14 +- src/views/community/CommunityView.vue | 2 +- src/views/encyclopedia/CloudTypeView.vue | 2 +- src/views/encyclopedia/EncyclopediaView.vue | 2 +- src/views/gallery/GalleryView.vue | 8 +- src/views/map/MapView.vue | 21 +- src/views/profile/ProfileSettingsView.vue | 3 +- src/views/profile/ProfileView.vue | 14 +- src/views/upload/UploadView.vue | 10 +- 26 files changed, 710 insertions(+), 453 deletions(-) diff --git a/.agents/skills/opencloud-style/SKILL.md b/.agents/skills/opencloud-style/SKILL.md index 87d6890..0ecd878 100644 --- a/.agents/skills/opencloud-style/SKILL.md +++ b/.agents/skills/opencloud-style/SKILL.md @@ -1,354 +1,235 @@ --- name: opencloud-style -description: "Use when modifying OpenCloud visual design, Tailwind classes, component styling, layout, typography, or page-level visual hierarchy. Ensures the project keeps its current clean sky-atlas visual language — airy, fresh, bright, observational — and avoids inconsistent dark or purple defaults." +description: "Maintain OpenCloud's current visual system when changing Vue templates, Tailwind classes, CSS, Naive UI theming, layout, typography, colors, buttons, cards, modals, maps, or responsive behavior. Preserve the clean sky-atlas language: airy sky-blue surfaces, fresh eucalyptus green, square atlas-like panels, pixel-cloud identity, and hard offset shadows." --- # OpenCloud Visual Style -Use this skill whenever changing OpenCloud visual design, component styling, Tailwind classes, layout, typography, or any page-level appearance. +Treat the current design tokens and shared `oc-*` classes as the visual contract. Keep the interface airy, bright, observational, and slightly editorial—like a field atlas for clouds. + +## Start From the Sources of Truth + +Inspect these files before making a material visual change: + +1. `src/styles/tokens.css` — palette remapping, semantic colors, gradients, shadows, motion. +2. `src/styles/base.css` — body baseline and the global square-corner rule. +3. `src/styles/components.css` — reusable page, surface, form, and interaction contracts. +4. `src/lib/theme.ts` — Naive UI overrides; keep semantic colors aligned with `tokens.css`. +5. `docs/style-system.md` — naming and implementation guidance. +6. `scripts/check-style-system.mjs` — prohibited legacy patterns. + +Use `src/style.css` only as the stylesheet assembly point. Put stable values in `tokens.css`, cross-page patterns in `components.css`, and truly component-specific behavior in scoped styles. ## Visual Direction -OpenCloud should feel like a clean sky atlas: airy, fresh, bright, and observational. +- Use white, slate-50, sky-50, and soft translucent white for primary surfaces. +- Use sky blue for atmosphere, page heroes, upload/create actions, and the pixel-cloud brand surface. +- Use fresh eucalyptus green for identity, navigation selection, account access, confirmation, and interactive hover states. +- Use leaf green only for explicit success, approved, public, or completed states. +- Use amber for pending/review/uncommon states and rose/red for destructive/rejected states. +- Keep slate-900 text dominant. Do not turn normal page sections into dark panels. +- Avoid purple, indigo, generic gray utilities, neon green, and decorative color proliferation. -- Base surfaces: `white`, `slate-50`, `sky-50`, soft translucent white (`bg-white/80`, `bg-white/88`). -- Main accent: teal/cyan for navigation, identity, account surfaces. -- Secondary accent: sky blue for upload and action highlights. -- Supporting accent: amber only for rarity, badges, pending/review states. -- Danger accent: rose/red only for destructive or rejected states. -- Avoid purple as a default accent. -- Avoid black selected states except for text or rare high-contrast badges. +## Current Palette ---- +Tailwind `teal-*` and `emerald-*` are compatibility names remapped in `tokens.css`; they are not Tailwind defaults. + +| Role | Main | Dark | Light surface | +| --- | --- | --- | --- | +| Eucalyptus brand/interaction | `#34745d` | `#274c40` | `#f2faf6` | +| Leaf success | `#4d7f3b` | `#35522c` | `#f4faef` | +| Sky information/action | `#0369a1` | `#075985` | `#e0f2fe` | +| Canvas | `#eef4f8` | — | `#f8fbfd` | +| Primary text | `#0f172a` | — | — | +| Secondary text | `#334155` | — | — | +| Muted text | `#64748b` | — | — | +| Border | `#d9e3ee` | — | — | + +Rules: + +- Do not use Tailwind `green-*` or `lime-*`. +- Do not reintroduce Tailwind's vivid teal values such as `#14b8a6` or `#0f766e`. +- Use semantic tokens or existing `teal-*`/`emerald-*` compatibility utilities instead of new hard-coded greens. +- Keep green text and actions dark enough for readable contrast; use pale green primarily as a surface. +- Synchronize changes to brand or success colors between `tokens.css` and `src/lib/theme.ts`. + +## Shape and Border Rules + +The product is square by default. `src/styles/base.css` forces Tailwind `rounded*` utilities to `0px`. + +- Use square corners for page panels, cards, buttons, inputs, navigation, dropdowns, map controls, and ordinary modals. +- Use a one-pixel slate or semantic-color border to define surfaces. +- Do not add rounded rectangles as a generic polish treatment. + +Documented exceptions: + +- Circular close/overlay controls in `ImageDetailModal`. +- Circular location/marker dots and AMap-generated marker bubbles. +- The media-focused `ImageDetailModal` shell. +- AMap-generated information cards and the deliberately organic timeline control details in `MapView`. + +Do not treat map floating buttons as exceptions: `.oc-map-button` is a square `40px × 40px` control with `0px` radius and a hard shadow, including its disabled state. + +## Shadows and Interaction + +Use hard offset shadows with zero blur as the signature depth treatment: + +| Token | Value | Typical use | +| --- | --- | --- | +| `--oc-shadow-xs` | `3px 3px` | Small controls and map buttons | +| `--oc-shadow-sm` | `4px 4px` | Header logo and compact CTAs | +| `--oc-shadow-md` | `6px 6px` | Standard cards and navigation | +| `--oc-shadow-lg` | `8px 8px` | Menus and floating panels | +| `--oc-shadow-xl` | `12px 12px` | Large system/auth cards | +| `--oc-shadow-sky` | sky-tinted `4px 4px` | Sky actions | +| `--oc-shadow-primary` | eucalyptus-tinted `4px 4px` | Green interactions | + +- On hover, interactive controls may move `translate(-1px, -1px)` and grow the hard shadow. +- On active, return the control to its origin. +- Preserve a visible `2px` sky focus outline with offset. +- Do not replace the hard-shadow language with generic soft shadows across ordinary UI. +- Soft/blurred shadows remain acceptable for media overlays, AMap internals, and image hover depth. + +## Backgrounds and Brand Identity + +Use the semantic gradients in `tokens.css`: + +- App shell: a subtle eucalyptus radial glow over the pale blue-gray canvas. +- Standard page hero: `#e0f2fe` to `#f8fafc`. +- Soft page hero: `#f2faf6` to `#f8fafc`. +- App grid: subtle slate lines at `32px`. + +The header identity is fixed: + +- Use the pixel-style cloud/sun SVG, not an emoji or unrelated icon set. +- Put the icon in a square `40px` container with `bg-sky-100`, `border-sky-200`, and a `4px 4px` neutral hard shadow. +- Keep “LIVE SKY ATLAS” as the small tracked eyebrow and “OpenCloud” as the bold brand name. +- Keep the header white/translucent; green belongs to navigation and account interaction, not the logo background. ## Typography -**Font stack** (`src/style.css`, `App.vue`): -``` +Use: + +```text "IBM Plex Sans", "Noto Sans SC", "PingFang SC", sans-serif ``` -Monospace: `"IBM Plex Mono", "SFMono-Regular", monospace` -**Type scale:** +Use IBM Plex Mono for small atlas-like labels when a monospace accent is appropriate. -| Size | Use | -|------|-----| -| `text-[11px]` | Header subtitle, heatmap weekday labels | -| `text-xs` | Uppercase section labels, captions, metadata, tags, help text | -| `text-sm` | Body text, descriptions, nav items, form labels, detail sections | -| `text-base` | Hero description paragraphs | -| `text-lg` | Logo brand name, card titles, item counters | -| `text-xl` | Modal titles, subsection headings, stat cards | -| `text-2xl` | Subsection headings, stat values, settings section titles | -| `text-3xl` | Stat values, avatar initials | -| `text-4xl` | Page H1 headings (all page heroes) | -| `text-5xl` | Login/Register hero text only | -| `text-6xl`+ | Decorative single characters, error page emoji | +- Page eyebrow: `text-sm`, uppercase, `tracking-[0.24em]`, sky-700. +- Page title: `text-4xl`, bold, slate-900. +- Descriptions: `text-sm`, `leading-7`, slate-600. +- Section/stat labels: `text-xs`, uppercase, tracking `0.18em–0.20em`, slate-500. +- Header brand eyebrow: `text-[11px]`, uppercase, `tracking-[0.22em]`. +- Reserve `font-black` and very large display text for authentication hero statements. -**Font weights:** -- `font-medium` (500) — body text, nav items, form labels, tags -- `font-semibold` (600) — modal titles, item names, emphasis -- `font-bold` (700) — headings, stat values, card titles -- `font-black` (900) — Login/Register hero only, with `leading-[1.05]` +## Layout and Responsive Behavior -**Letter tracking:** +Prefer the shared page structure: -| Tracking | Where | -|----------|-------| -| `tracking-[0.12em]` | Nav links, upload/button text (uppercase) | -| `tracking-[0.18em]` | Info panel section labels | -| `tracking-[0.20em]` | Stat card labels | -| `tracking-[0.22em]` | Header subtitle, modal subtitle, quick-upload header | -| `tracking-[0.24em]` | Page hero section labels | -| `tracking-[0.26em]` | Login/Register hero labels | +```html +
+
+

Cloud Upload

+

上传云图

+

页面描述

+
+
---- - -## Border Radius - -**All border-radius is `0px` by default** — enforced via Naive UI theme override and global CSS: - -```css -[class~='rounded'], [class*='rounded-'] { border-radius: 0 !important; } +
+ +
``` -Only exceptions (all `!important` or explicit inline): -- Close/map buttons: `rounded-full` / `border-radius: 9999px` -- ImageDetailModal shell: `rounded-[30px]` -- MapView info panel cards: `rounded-2xl` -- MiniLocationMap marker dot: `rounded-full` -- UploadView native inputs: `rounded-lg` +Container widths: ---- +- `oc-container--narrow` — `max-w-4xl` +- `oc-container--content` — `max-w-6xl` +- `oc-container--wide` — `max-w-7xl` -## Shadows +Use `px-4 sm:px-6 lg:px-8` for page gutters. Validate at both desktop and a real `375px` mobile viewport. Do not accept horizontal overflow. -**Signature pattern: hard offset box shadows** — `shadow-[Xpx_Xpx_0_0_rgba(...)]` with zero blur. +## Reusable Component Contracts -| Shadow | Offset | Use | -|--------|--------|-----| -| `shadow-[3px_3px_0_0_rgba(...)]` | 3/3 | Small header buttons, login button | -| `shadow-[4px_4px_0_0_rgba(...)]` | 4/4 | Logo box, primary CTAs, hidden header, profile avatar | -| `shadow-[6px_6px_0_0_rgba(...)]` | 6/6 | Navigation bar, search box | -| `shadow-[8px_8px_0_0_rgba(...)]` | 8/8 | Dropdown menus, archive panel, account card | -| `shadow-[10px_10px_0_0_rgba(...)]` | 10/10 | Login/Register hero sections | -| `shadow-[12px_12px_0_0_rgba(...)]` | 12/12 | Login/Register cards, CommunityView section, AuthConfirm card | +Use existing semantic classes before writing repeated Tailwind bundles: -**Shadow colors by context:** -- `rgba(15,23,42,...)` — slate-900, neutral (most common) -- `rgba(14,165,233,...)` — sky-500 (sky-colored buttons) -- `rgba(20,184,166,...)` — teal-500 (teal accents) +- Page structure: `oc-page-hero`, `oc-page-hero--soft`, `oc-container`, `oc-page-content`. +- Typography: `oc-page-eyebrow`, `oc-page-title`, `oc-page-description`, `oc-section-label`, `oc-stat-label`, `oc-stat-value`, `oc-meta`. +- Forms: `oc-field-label`, `oc-field-required`, `oc-field-control`, `oc-field-help`, `oc-field-error`. +- Surfaces: `oc-surface`, `oc-inset-panel`, `oc-panel-card`, `oc-panel-card-soft`, `oc-empty-card`. +- Interaction: `oc-primary-button`, `oc-panel-button`, `oc-header-button`, `oc-icon-button`, `oc-map-button`, `oc-choice-button`, `oc-text-button`, `oc-menu-item`, `oc-nav-link`. -**Standard Tailwind shadows** also used sparingly: -- `shadow-sm` — stat cards, gallery cards, encyclopedia cards -- `shadow-lg` — cloud card hover lift -- `shadow-2xl` — modal shells +Class names describe function. Keep modifiers attached to their base class, for example: ---- - -## Gradients - -All page backgrounds use gradients — avoid flat single-color backgrounds. - -| Gradient | Where | -|----------|-------| -| `linear-gradient(180deg,#e0f2fe_0%,#f8fafc_100%)` | Page hero sections (standard) | -| `linear-gradient(180deg,#f0fdfa_0%,#f8fafc_100%)` | Community page, soft app pages | -| `linear-gradient(135deg,#f0fdfa_0%,#fff_48%,#eff6ff_100%)` | Login hero | -| `linear-gradient(135deg,#eff6ff_0%,#fff_42%,#f0fdfa_100%)` | Register hero | -| `linear-gradient(180deg,#f8fafc_0%,#eef6ff_55%,#f8fafc_100%)` | Admin page | -| `linear-gradient(135deg,#ecfeff_0%,#ccfbf1_100%)` | Brand chip/avatar | -| `linear-gradient(180deg,#f0fdfa_0%,#fff_100%)` | Account card header | - -**App shell background** (`App.vue`): -``` -radial-gradient(circle_at_top_left, rgba(12,148,136,0.12), transparent 28%) -linear-gradient(180deg, #f8fbfd 0%, #eef4f8 100%) -``` -Plus a dotted grid overlay: `bg-[size:32px_32px] opacity-35`. - -**Body background** (`style.css`): -``` -linear-gradient(180deg, rgba(15,23,42,0.02), rgba(15,23,42,0)), #eef4f8 +```html + + 保存 + ``` ---- +Available shared variants: -## Layout +- Primary: `oc-primary-button--teal`, `oc-primary-button--sky` +- Panel: `--neutral`, `--sky`, `--teal`, `--amber`, `--danger` +- Header: `--neutral`, `--sky`, `--teal` +- Icon: `--small`, `--tiny`, `--sky`, `--danger` +- Text/menu: muted, amber, and danger variants where defined -**Page max-widths:** -- `max-w-7xl` — AppHeader, GalleryView, AdminView -- `max-w-6xl` — UploadView, ProfileView, EncyclopediaView -- `max-w-4xl` — ProfileSettingsView, CommunityView -- `max-w-3xl` — QuickUploadModal, MapPickerModal, AuthConfirmView -- `max-w-2xl` — ResetPasswordView, NotFoundView, ForbiddenView, CloudEditModal +## Button Semantics -**Page hero pattern** (consistent across all views): -``` -border-b border-sky-100 bg-[linear-gradient(180deg,#e0f2fe_0%,#f8fafc_100%)] - max-w-{6xl|7xl} mx-auto px-4 py-10 - text-sm font-medium uppercase tracking-[0.24em] text-sky-700 (subtitle) - h1 mt-3 text-4xl font-bold text-slate-900 (title) - p mt-4 max-w-2xl text-sm leading-7 text-slate-600 (description) -``` +- Teal/eucalyptus: login, account access, active toggle, confirm. +- Sky: register, create, upload, forward action. +- Neutral: cancel and utility actions. +- Amber: pending/review/state-toggle actions. +- Danger: deletion and destructive actions. -**Horizontal padding:** `px-4` / `sm:px-6` / `lg:px-8` +For Naive UI buttons: -**Column systems:** -- Gallery masonry: `columns-2 gap-3 md:columns-3 xl:columns-4 [column-gap:0.75rem]` -- Card grids: `gap-4 md:grid-cols-2 xl:grid-cols-3` or `gap-4 md:grid-cols-4` -- Two-column splits: `lg:grid-cols-[1.1fr_0.9fr]`, `lg:grid-cols-[1.4fr_0.9fr]` +- Use `type="default"` with the shared `oc-*` class when a custom visual variant is intended. +- Do not rely on Naive UI default primary/secondary styling for page CTAs or panel actions. +- Keep `src/lib/theme.ts` as the global Naive UI contract; it is mounted by `App.vue`. +- Use dark text on light primary surfaces. Do not assume primary buttons should have white text. -**Gap scale:** `gap-4`, `gap-5`, `gap-6` for card grids; `gap-2` for nav items; `space-y-6`/`space-y-8` between sections. +## Icons, Cards, and Modals ---- +- Use `@vicons/tabler` through `NIcon` for interface icons. +- Preserve the pixel-cloud SVG for the primary brand mark. +- Use one bordered surface and one appropriate hard-shadow token for ordinary cards. +- Keep modal structure square unless it is a documented media exception. +- Use `oc-overlay-button` for controls over dark media and `oc-icon-button` for controls on light surfaces. +- Keep semantic status colors stable: emerald success, amber pending, rose rejected/destructive, slate hidden/neutral. -## Component Conventions +## Implementation Workflow -### Buttons - -**Primary CTA (upload, action):** -``` -inline-flex h-8 items-center border border-sky-200 bg-sky-100 px-3 -text-sm font-medium uppercase tracking-[0.12em] text-sky-800 -shadow-[4px_4px_0_0_rgba(14,165,233,0.14)] -transition-colors hover:bg-sky-50 hover:text-sky-900 -``` -Mobile: `h-8 md:h-10 md:px-4` - -**Secondary/neutral:** -``` -inline-flex h-8 items-center border border-slate-200 bg-white/80 px-3 -text-sm font-medium text-slate-700 -shadow-[3px_3px_0_0_rgba(15,23,42,0.06)] -hover:border-teal-200 hover:bg-teal-50 hover:text-teal-800 -``` - -**Danger/delete:** `text-rose-600 hover:bg-rose-50 hover:text-rose-700` - -**Map floating buttons:** `w-10 h-10 bg-white rounded-lg shadow-md flex items-center justify-center hover:bg-gray-50` - -**Naive UI buttons** use `secondary strong` as default; `type="primary"` for primary actions; `block size="large"` for block forms. - -### Cards - -**Standard card:** -``` -border bg-white text-left shadow-sm -transition-all hover:-translate-y-1 hover:shadow-lg -``` - -**Encyclopedia unlocked variant:** `border-amber-300 shadow-amber-100/60` - -**Naive UI NCard:** `class="border border-slate-200 shadow-sm"` - -### Modals - -**Overlay:** `fixed inset-0 z-[110|130] bg-slate-950/60` (or `bg-black/85` for image detail) - -**Shell:** `bg-white shadow-2xl` with max-w matching content width. ImageDetailModal uses `rounded-[30px]`; others are square. - -**Close button:** Circular (`rounded-full`, `border-radius: 9999px`), semi-transparent background. - -**Sections:** `border-b border-slate-200` separates header/body/footer. - -### Inputs & Forms - -**Native HTML inputs:** -``` -border border-slate-300 px-3 py-2.5 text-sm -focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-500 -``` - -**Labels:** `block text-sm font-medium text-slate-700 mb-1` - -**Required marker:** `*` - -**Error text:** `text-sm text-red-500 mt-1` - -**Checkboxes:** `h-5 w-5 border-slate-300 text-sky-500 focus:ring-sky-500` - -### Tags & Badges - -All use `inline-flex border px-3 py-1 text-xs font-medium` with color variants: - -- `common`: `bg-sky-100 text-sky-700 border-sky-200` -- `uncommon`: `bg-amber-100 text-amber-700 border-amber-200` -- `rare`: `bg-rose-100 text-rose-700 border-rose-200` -- `approved`/`success`: `bg-emerald-100 text-emerald-700 border-emerald-200` -- `pending`: `bg-amber-100 text-amber-700 border-amber-200` -- `rejected`: `bg-rose-100 text-rose-700 border-rose-200` -- `hidden`/neutral: `bg-slate-100 text-slate-600` - -### Menus & Dropdowns - -**Account dropdown:** -``` -border border-slate-200 bg-white shadow-[8px_8px_0_0_rgba(15,23,42,0.08)] -``` - -**Menu items:** -``` -flex items-center gap-3 px-4 py-2.5 text-sm font-medium text-slate-700 -transition-colors hover:bg-teal-50 hover:text-teal-800 -``` - -**Danger menu item:** `text-rose-600 hover:bg-rose-50 hover:text-rose-700` - -### Navigation - -**Header:** `sticky top-0 z-50 h-28 md:h-16 bg-white/88 backdrop-blur-xl border-b border-slate-200/80`, scroll-hides with `transition-transform duration-300`. - -**Selected nav item:** `bg-teal-100 text-teal-800 ring-1 ring-teal-200` - ---- - -## Core Palette - -Use these Tailwind families first: - -- `slate` — text, borders, neutral panels -- `sky` — page gradients, upload/action emphasis, map/cloud atmosphere -- `teal` — navigation selected states, account identity, calm success-adjacent UI -- `cyan` — subtle sky gradients and avatar/logo surfaces -- `emerald` — explicit success/approved state only -- `amber` — pending/review/uncommon rarity -- `rose` — rejected/destructive/rare rarity - -**Key hex values:** - -| Role | Value | -|------|-------| -| Page background | `#eef4f8` / `#f3f7fb` | -| Body text | `#0f172a` (slate-900) | -| Secondary text | `#334155` (slate-700) | -| Muted text | `#64748b` (slate-500) | -| Borders | `#d9e3ee`, `#e2e8f0`, `#cbd5e1` | -| Selection | `rgba(15,118,110,0.18)` (teal-700) | -| Text on primary | `#0f172a` (dark, not white — Naive UI override) | - -**Naive UI theme tokens** (`App.vue`): -``` -primaryColor: '#0f766e' (teal-700) -infoColor: '#0369a1' (sky-700) -successColor: '#0f766e' (teal-700) -warningColor: '#d97706' (amber-600) -errorColor: '#dc2626' (red-600) -``` - ---- - -## Animation & Transitions - -**Hover effects:** -- Cards: `transition-all hover:-translate-y-1 hover:shadow-lg` (lift) -- Images: `transition duration-500 group-hover:scale-[1.04]` (subtle zoom) -- Gallery items: `transition-transform duration-300 hover:-translate-y-1` -- Badges on cards: `transition-opacity md:opacity-0 md:group-hover:opacity-100` - -**Modal transitions:** -- ImageDetailModal: opacity 180ms, shell scale+translateY 220ms, `cubic-bezier(0.22, 1, 0.36, 1)` -- Other modals: default Vue transitions - -**Header scroll:** `transition-transform duration-300` - -**Image loading:** `transition-opacity duration-300` for thumbnail-to-hires crossfade, low-res preview gets `blur-[1px]` until full-res loads. - -**Skeleton shimmer:** -``` -animate-pulse bg-[linear-gradient(110deg,#0f172a_0%,#1e293b_45%,#334155_55%,#0f172a_100%)] -bg-[length:220%_100%] -``` - ---- - -## Icons - -**Library:** `@vicons/tabler` (Tabler Icons), rendered via `` from Naive UI. - -Common icons: `User`, `Settings`, `Logout`, `Shield`, `Search`, `X`, `Clock`, `Location`/`MapIcon`, `CloudUpload`, `Map`/`Satellite`, `Adjustments`, `Calendar`, `Refresh`, `InfoCircle`, `Lock`, `Check`, `Eye`/`EyeOff`, `Trash` - ---- +1. Inspect the source-of-truth files and the target component. +2. Reuse an existing semantic class if it expresses the intent. +3. Use Tailwind only for one-off layout and responsive composition. +4. Add a token before repeating a stable raw value. +5. Add a shared `oc-*` class only for cross-page patterns or product-wide rules. +6. Keep feature-specific AMap, slider, media, or transition details scoped to the component. +7. When changing a global color, audit all matching Tailwind utilities, hard-coded hex/RGB values, gradients, shadows, canvas drawing, and Naive UI tokens. +8. Preserve unrelated user changes in the worktree. ## Anti-Patterns -- Do not use purple as a fallback visual direction. -- Do not use `bg-slate-900 text-white` for ordinary selected navigation. -- Do not introduce dark-mode-heavy sections unless the surrounding page already uses that language. -- Do not overuse rounded rectangles; the project uses sharp, atlas-like panels. -- Do not mix random accent colors within the same feature. Pick one accent family and keep it consistent. -- Do not use flat single-color backgrounds where a gradient belongs. -- Do not add border-radius without an explicit reason — `0px` is the project default. -- Do not use white text on primary buttons — Naive UI primary buttons use dark text (`#0f172a`). - ---- +- Default Tailwind `green-*`, `lime-*`, vivid teal, purple, indigo, or generic `gray-*`. +- Ad hoc rounded cards, pills, or map buttons. +- Soft drop shadows replacing hard atlas shadows across normal UI. +- New button styling copied into templates instead of shared `oc-*` variants. +- `NButton type="primary"` used as a shortcut around the shared button contract. +- White text on pale green or sky action surfaces. +- Emoji used as the site identity. +- Flat page backgrounds where the shared hero/canvas gradient applies. +- Green used indiscriminately for both brand interaction and success state. ## Validation After style changes, run: ```bash -npx vue-tsc -b +npm run check:styles +git diff --check +npm run build ``` -Run `npm run build` if the change touches routes, imported components, or package dependencies. +For material visual changes, render representative pages and inspect desktop and `375px` mobile screenshots. Check computed styles when shape, shadow, color token, or responsive overflow is part of the request. diff --git a/AGENTS.md b/AGENTS.md index 9bc3121..1137cf9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,9 +3,11 @@ ## Commands - `npm run dev` — Vite dev server with HMR +- `npm run check:styles` — validates shared style contracts and blocks deprecated palettes/classes - `npm run build` — `vue-tsc -b && vite build` (typecheck must pass or build aborts) - `npx vue-tsc -b` — standalone typecheck (no script in package.json) -- No linter, formatter, or test runner exists +- `npm run preview` — previews the production build on strict port `5173` +- No general linter, formatter, or test runner exists; `check:styles` is the project-specific visual-system check ## Architecture @@ -26,9 +28,13 @@ | `src/components/cloud/` | Cloud-related modals and widgets: ImageDetailModal, CloudEditModal, MapPickerModal, QuickUploadModal, MiniLocationMap | | `src/components/layout/` | AppHeader (top nav bar with auth state) | | `src/components/profile/` | ContributionHeatmap | -| `src/style.css` | Global visual system hooks: shared button/card utility classes, base typography, body background | +| `src/style.css` | Global stylesheet assembly point; imports Tailwind, tokens, base rules, and shared components | +| `src/styles/` | Visual system source: semantic tokens, global square-corner baseline, shared `oc-*` component contracts | +| `src/lib/theme.ts` | Naive UI theme overrides aligned with the semantic tokens | | `src/views/` | Route-level page components (see Routes below) | | `src/types/` | TypeScript types: domain models (`database.ts`), API DTOs (`api.ts`), AMap declarations (`amap.d.ts`), router meta (`router.d.ts`) | +| `docs/style-system.md` | Concise implementation and naming guide for the visual system | +| `scripts/check-style-system.mjs` | Automated guard against deprecated palettes and malformed shared classes | ## Routes @@ -126,12 +132,33 @@ Optional: Backend-only variables such as database credentials, JWT secrets, SMTP settings, cookie policy, CORS origins, and upload paths belong in `../opencloud-backend/.env`, never in the Vite environment. +## Visual System + +- The design language is a clean sky atlas: airy sky-blue surfaces, fresh eucalyptus green, square editorial panels, pixel-cloud identity, and hard offset shadows. +- Read `.agents/skills/opencloud-style/SKILL.md` before material visual changes. Treat `src/styles/tokens.css`, `src/styles/base.css`, `src/styles/components.css`, and `src/lib/theme.ts` as the implementation sources of truth. +- Shared/stable decisions belong in layers: + - tokens and palette remapping → `src/styles/tokens.css` + - global baseline and square-corner policy → `src/styles/base.css` + - cross-page semantic classes → `src/styles/components.css` + - feature-specific map/slider/media details → the component's scoped style + - one-off layout and responsive composition → Tailwind in the template +- The global interface is square by default. Ordinary cards, inputs, dropdowns, buttons, modals, and map controls use `0px` radius. Circular marker dots, media overlay controls, AMap-generated bubbles/cards, and the media-focused image-detail shell are explicit exceptions. +- Use hard zero-blur offset shadows (`--oc-shadow-xs` through `--oc-shadow-xl`) for ordinary UI depth. Interactive controls may lift by `translate(-1px, -1px)` on hover. +- Brand/interaction green is fresh eucalyptus: main `#34745d`, dark `#274c40`, light surface `#f2faf6`. +- Success green is a distinct leaf green: main `#4d7f3b`, dark `#35522c`, light surface `#f4faef`. +- Existing `teal-*` and `emerald-*` Tailwind class names are compatibility aliases remapped in `tokens.css`; do not assume Tailwind's default values. +- Do not introduce Tailwind `green-*`/`lime-*`, vivid default teal, purple, indigo, or generic gray palettes. Use slate, sky, remapped teal/emerald, amber, and rose according to semantic purpose. +- The header brand mark is the pixel cloud/sun SVG inside a square `40px` `sky-100` surface with a `sky-200` border and neutral `4px` hard shadow. Do not replace it with emoji or give it a green background. +- Map floating buttons use `oc-map-button`: square `40px × 40px`, white background, slate border, `0px` radius, and a hard shadow even when disabled. +- For material visual changes, inspect representative desktop and real `375px` mobile screenshots and verify there is no horizontal overflow. + ## Naive UI - Used for modals, buttons, inputs, tags, progress bars, alerts, dropdowns, empty states, skeletons, and message toasts. - No SSR — pure client-side rendering. - Custom CSS overrides via scoped `