Files
opencloud/.agents/skills/opencloud-style/SKILL.md
T

12 KiB

name, description
name description
opencloud-style 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.

OpenCloud Visual Style

Use this skill whenever changing OpenCloud visual design, component styling, Tailwind classes, layout, typography, or any page-level appearance.

Visual Direction

OpenCloud should feel like a clean sky atlas: airy, fresh, bright, and observational.

  • 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.

Typography

Font stack (src/style.css, App.vue):

"IBM Plex Sans", "Noto Sans SC", "PingFang SC", sans-serif

Monospace: "IBM Plex Mono", "SFMono-Regular", monospace

Type scale:

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

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]

Letter tracking:

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

Border Radius

All border-radius is 0px by default — enforced via Naive UI theme override and global 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

Shadows

Signature pattern: hard offset box shadowsshadow-[Xpx_Xpx_0_0_rgba(...)] with zero blur.

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

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)

Standard Tailwind shadows also used sparingly:

  • shadow-sm — stat cards, gallery cards, encyclopedia cards
  • shadow-lg — cloud card hover lift
  • shadow-2xl — modal shells

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

Layout

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

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)

Horizontal padding: px-4 / sm:px-6 / lg:px-8

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]

Gap scale: gap-4, gap-5, gap-6 for card grids; gap-2 for nav items; space-y-6/space-y-8 between sections.


Component Conventions

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: <span class="text-red-500">*</span>

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 <NIcon size="16|18|20|22|30|42"> 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


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).

Validation

After style changes, run:

npx vue-tsc -b

Run npm run build if the change touches routes, imported components, or package dependencies.