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, tagsfont-semibold(600) — modal titles, item names, emphasisfont-bold(700) — headings, stat values, card titlesfont-black(900) — Login/Register hero only, withleading-[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 shadows — shadow-[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 cardsshadow-lg— cloud card hover liftshadow-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, AdminViewmax-w-6xl— UploadView, ProfileView, EncyclopediaViewmax-w-4xl— ProfileSettingsView, CommunityViewmax-w-3xl— QuickUploadModal, MapPickerModal, AuthConfirmViewmax-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-3orgap-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-200uncommon:bg-amber-100 text-amber-700 border-amber-200rare:bg-rose-100 text-rose-700 border-rose-200approved/success:bg-emerald-100 text-emerald-700 border-emerald-200pending:bg-amber-100 text-amber-700 border-amber-200rejected:bg-rose-100 text-rose-700 border-rose-200hidden/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 panelssky— page gradients, upload/action emphasis, map/cloud atmosphereteal— navigation selected states, account identity, calm success-adjacent UIcyan— subtle sky gradients and avatar/logo surfacesemerald— explicit success/approved state onlyamber— pending/review/uncommon rarityrose— 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-whitefor 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 —
0pxis 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.