diff --git a/crates/op-ai-skills/skills/domains/mobile-app.md b/crates/op-ai-skills/skills/domains/mobile-app.md index d7d55a0cd..329387f5e 100644 --- a/crates/op-ai-skills/skills/domains/mobile-app.md +++ b/crates/op-ai-skills/skills/domains/mobile-app.md @@ -43,10 +43,12 @@ Rules: - One primary intent per screen. Everything else is subordinate. - First 1-2 elements must answer "where am I" + "what can I do here" +- Mobile top rhythm: keep the title/header group close to the first useful control or content module. On 375-430px screens, the gap from header/title to search, primary action, chart, or first card should usually be 20-32px. Do not leave an empty hero-sized band unless the prompt explicitly asks for an editorial hero. - Title font size must be uniform across ALL screens in the app - Design for one-handed use: primary actions in lower half - Single vertical scroll (avoid nested scrolls) - Touch targets: minimum 44x44px +- Do not repeat the same predictable mobile stack of search + categories + orange promo + two cards. Choose a distinct concept for the domain and make one signature moment carry the personality. DO NOT: @@ -54,35 +56,35 @@ DO NOT: - Use spacer elements for bottom space (use padding-bottom) - Cram multiple competing sections above the fold -## 3) BOTTOM TAB BAR — PILL STYLE +## 3) BOTTOM TAB BAR — OPTIONAL, INTEGRATED + +Do not force bottom navigation into every mobile screen. Use a bottom tab bar only when the product clearly has persistent top-level destinations (Home, Search, Orders, Profile, etc.). If the screen is a single-task flow, omit bottom navigation. Tab Bar Container: -- Full screen width -- Padding: [12, 21, 21, 21] (includes home-indicator safe area) -- Fill: gradient overlay (transparent at top → solid background at 30%) +- Full screen width and part of the page flow, not a floating overlay +- Height: 62-72px including safe-area breathing room +- Background: same page palette or a subtle tonal surface +- Separation: quiet 1px divider or tonal contrast only; no detached shadow band -Pill (menu items wrapper): +Nav Surface: -- Height: 62px, width: fill_container -- Corner radius: 36px -- Border: 1px solid (theme border color) -- Inner padding: 4px +- Use role="bottom-tab-bar" +- Not a floating pill, not a nested rounded capsule, and not a separate footer band +- Direct tab item frames stay transparent: no fill, no stroke, no large rounded tile Tab Items (3-5 tabs, top-level destinations only): - Width: fill_container, height: fill_container -- Corner radius: 26px - Layout: vertical, gap: 4, centered on both axes - Icon: 18px -- Label: 10px, weight 500-600, UPPERCASE, letterSpacing: 0.5 +- Label: 10-11px, weight 500-600, letterSpacing: 0 -Active state: solid fill (accent color) + contrasting icon/label color +Active state: accent icon/label color or a tiny 2-3px indicator Inactive state: transparent background + muted icon/label color Rules: -- Labels MUST be uppercase - Tab switching preserves each tab's navigation state - App content must never be obscured by the Tab Bar @@ -92,7 +94,7 @@ Before generating nodes, mentally verify these three layers are accounted for: 1. Status Bar: standard or edge-to-edge? 2. App Content: what is the header, primary content, action placement, scroll behavior? -3. Bottom Bar: None or Pill Tab Bar (which tabs)? +3. Bottom Bar: None or integrated tab bar (which tabs)? Do NOT output this blueprint as text. Apply it silently through your node structure. Your output must remain valid JSON/JSONL only. diff --git a/crates/op-ai-skills/skills/knowledge/role-definitions.md b/crates/op-ai-skills/skills/knowledge/role-definitions.md index b4583dd77..0c6f8c30c 100644 --- a/crates/op-ai-skills/skills/knowledge/role-definitions.md +++ b/crates/op-ai-skills/skills/knowledge/role-definitions.md @@ -79,7 +79,7 @@ Media roles: Layout-escape roles: -- overlay: the ONLY way to place a child at absolute x/y inside a parent that uses `layout: vertical|horizontal`. Use for notification dots on an icon, corner ribbons on a card, floating status indicators. The child keeps its explicit `x`/`y` while siblings flow normally. Do NOT use `role: 'overlay'` for inline components — `badge`, `pill`, `tag` are inline (they flow in layout like any other child, NOT floating). Do NOT use `role: 'overlay'` as a substitute for `layout: 'none'` on the parent. +- overlay: the ONLY way to place a child at absolute x/y inside a parent that uses `layout: vertical|horizontal`. Use only for bounded in-parent overlays such as notification dots on an icon, card badges, or status indicators that must stay fully inside their parent. The child keeps its explicit `x`/`y` while siblings flow normally, but it must not use negative x/y, protrude outside rounded corners, or overlap unrelated section chrome. A product-card favorite/heart must stay fully inside its card or image with an 8-12px inset, usually as an `icon-button`; never straddle the card edge. Do NOT use `role: 'overlay'` for inline components — `badge`, `pill`, `tag` are inline (they flow in layout like any other child, NOT floating). Do NOT use `role: 'overlay'` as a substitute for `layout: 'none'` on the parent. Typography roles: diff --git a/crates/op-ai-skills/skills/style-guides/clean-blue-mobile-light.md b/crates/op-ai-skills/skills/style-guides/clean-blue-mobile-light.md index 9d549714f..d47269739 100644 --- a/crates/op-ai-skills/skills/style-guides/clean-blue-mobile-light.md +++ b/crates/op-ai-skills/skills/style-guides/clean-blue-mobile-light.md @@ -14,7 +14,7 @@ Key aesthetics: - **Trusted blue accent**: #2563EB as the sole accent, conveying professionalism and reliability - **iOS-native typography**: SF Pro throughout, matching the platform's system font for seamless integration - **Comfortable radii**: 12-16px corners feel natural and inviting without being bubbly -- **Pill tab bar**: 100px radius bottom navigation with blue active indicator +- **Integrated bottom nav**: full-width bottom navigation with blue active indicator - **Clean separation**: Light borders (#E5E7EB) and subtle surfaces provide structure without visual weight ## Color System @@ -27,7 +27,7 @@ Key aesthetics: | Card Surface | #FFFFFF | Cards, list containers | | Inset Surface | #F3F4F6 | Search bars, input backgrounds, grouped sections | | Subtle Surface | #F9FAFB | Alternating list rows, secondary areas | -| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill | +| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface | ### Text Colors @@ -153,7 +153,7 @@ Key aesthetics: | 16px | Modal sheets, feature cards, hero containers | Maximum standard radius | | 100px | Tab bar pill, toggle pills, round avatars | Full capsule shape for navigation | -Design rationale: The 8-16px range mirrors iOS design conventions, where corners are soft enough to feel friendly but restrained enough to maintain professionalism. Every interactive element feels natural on a mobile device, with the progressive scale reinforcing visual hierarchy. The 100px pill for the tab bar provides the signature bottom navigation capsule. +Design rationale: The 8-16px range mirrors iOS design conventions, where corners are soft enough to feel friendly but restrained enough to maintain professionalism. Every interactive element feels natural on a mobile device, with the progressive scale reinforcing visual hierarchy. The bottom navigation should feel integrated with the page palette, using accent color or a tiny indicator for active state. ## Icons diff --git a/crates/op-ai-skills/skills/style-guides/dark-bold-mobile.md b/crates/op-ai-skills/skills/style-guides/dark-bold-mobile.md index dd21220ab..0cd5322a4 100644 --- a/crates/op-ai-skills/skills/style-guides/dark-bold-mobile.md +++ b/crates/op-ai-skills/skills/style-guides/dark-bold-mobile.md @@ -15,7 +15,7 @@ Key aesthetics: - **Gradient cards**: Program and feature cards use dark gradient fills for depth and visual interest - **Glow effects**: Lime-tinted shadows and glow indicators reinforce the neon aesthetic - **Soft industrial radii**: 8-24px corners balance the geometric sharpness with comfortable touch targets -- **Pill tab bar**: 100px radius bottom navigation with lime active indicator +- **Integrated bottom nav**: full-width bottom navigation with lime active indicator ## Color System @@ -28,7 +28,7 @@ Key aesthetics: | Elevated Surface | #1A1A1A | Elevated cards, modal sheets | | Gradient Start | #141414 | Gradient card dark edge | | Gradient End | #1E1E1E | Gradient card lighter edge | -| Tab Bar Surface | #141414 | Bottom navigation pill fill | +| Tab Bar Surface | #141414 | Integrated bottom navigation surface | ### Text Colors @@ -163,7 +163,7 @@ Key aesthetics: | 24px | Hero cards, prominent feature sections | Maximum standard radius | | 100px | Tab bar pill, toggle pills, round buttons | Full capsule shape for navigation | -Design rationale: Soft industrial radii (8-24px) balance the bold, geometric typography with comfortable touch targets. The range is wider than the terminal style (3-12px) but more restrained than playful (14-26px), reflecting an urban-industrial aesthetic that is confident without being aggressive. The 100px pill for the tab bar provides the signature bottom navigation capsule. +Design rationale: Soft industrial radii (8-24px) balance the bold, geometric typography with comfortable touch targets. The range is wider than the terminal style (3-12px) but more restrained than playful (14-26px), reflecting an urban-industrial aesthetic that is confident without being aggressive. The bottom navigation should feel integrated with the page palette, using accent color or a tiny indicator for active state. ## Icons diff --git a/crates/op-ai-skills/skills/style-guides/finance-clean-mobile-light.md b/crates/op-ai-skills/skills/style-guides/finance-clean-mobile-light.md index 82261d13e..fd25cf55b 100644 --- a/crates/op-ai-skills/skills/style-guides/finance-clean-mobile-light.md +++ b/crates/op-ai-skills/skills/style-guides/finance-clean-mobile-light.md @@ -15,7 +15,7 @@ Key aesthetics: - **Dual-purpose type**: DM Sans for headers and body, DM Mono for financial data and figures - **Professional radii**: 8-12px corners keep containers structured and business-appropriate - **Data-first layout**: Generous spacing around numbers, clear visual hierarchy for financial metrics -- **Pill tab bar**: 100px radius bottom navigation with teal active indicator +- **Integrated bottom nav**: full-width bottom navigation with teal active indicator ## Color System @@ -27,7 +27,7 @@ Key aesthetics: | Card Surface | #FFFFFF | Cards, account containers, transaction items | | Inset Surface | #F1F5F9 | Input backgrounds, search bars, grouped sections | | Subtle Surface | #F0FDFA | Positive balance highlight, growth indicators | -| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill | +| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface | ### Text Colors diff --git a/crates/op-ai-skills/skills/style-guides/health-minimal-mobile-dark.md b/crates/op-ai-skills/skills/style-guides/health-minimal-mobile-dark.md index bb35876e9..4b23794f1 100644 --- a/crates/op-ai-skills/skills/style-guides/health-minimal-mobile-dark.md +++ b/crates/op-ai-skills/skills/style-guides/health-minimal-mobile-dark.md @@ -15,7 +15,7 @@ Key aesthetics: - **Invisible type**: Inter at medium weights—so readable it disappears, letting data speak - **Minimal radii**: 12-16px corners are clean and functional without unnecessary roundness - **Data clarity**: Generous whitespace around metrics, clear hierarchy for at-a-glance reading -- **Pill tab bar**: 100px radius bottom navigation with green active indicator +- **Integrated bottom nav**: full-width bottom navigation with green active indicator ## Color System @@ -27,7 +27,7 @@ Key aesthetics: | Card Surface | #1A1A1A | Standard cards, metric containers | | Elevated Surface | #222222 | Elevated cards, modal sheets | | Subtle Surface | #161616 | Inset areas, grouped sections | -| Tab Bar Surface | #1A1A1A | Bottom navigation pill fill | +| Tab Bar Surface | #1A1A1A | Integrated bottom navigation surface | ### Text Colors diff --git a/crates/op-ai-skills/skills/style-guides/industrial-mobile-dark.md b/crates/op-ai-skills/skills/style-guides/industrial-mobile-dark.md index 515da31c3..a504b0710 100644 --- a/crates/op-ai-skills/skills/style-guides/industrial-mobile-dark.md +++ b/crates/op-ai-skills/skills/style-guides/industrial-mobile-dark.md @@ -15,7 +15,7 @@ Key aesthetics: - **Monospace precision**: JetBrains Mono for headings and data reinforces the technical identity - **Sharp corners**: 4-8px radii create a compact, engineered feel with minimal decoration - **Data density**: Tighter spacing and smaller type for information-heavy screens -- **Pill tab bar**: 100px radius bottom navigation with amber active indicator +- **Integrated bottom nav**: full-width bottom navigation with amber active indicator ## Color System @@ -27,7 +27,7 @@ Key aesthetics: | Card Surface | #27272A | Standard cards, list items | | Elevated Surface | #3F3F46 | Elevated cards, modal sheets | | Code Surface | #1E1E22 | Code blocks, terminal output areas | -| Tab Bar Surface | #27272A | Bottom navigation pill fill | +| Tab Bar Surface | #27272A | Integrated bottom navigation surface | ### Text Colors diff --git a/crates/op-ai-skills/skills/style-guides/luxury-fashion-mobile-dark.md b/crates/op-ai-skills/skills/style-guides/luxury-fashion-mobile-dark.md index 33cc7102f..219811558 100644 --- a/crates/op-ai-skills/skills/style-guides/luxury-fashion-mobile-dark.md +++ b/crates/op-ai-skills/skills/style-guides/luxury-fashion-mobile-dark.md @@ -15,7 +15,7 @@ Key aesthetics: - **Editorial serif display**: Cormorant Garamond at medium weight for elegant, fashion-magazine headings - **Sharp precision**: 4-8px corners reflect the deliberate geometry of luxury packaging - **High contrast**: White text on dark backgrounds with gold punctuation for dramatic hierarchy -- **Pill tab bar**: 100px radius bottom navigation with gold active indicator +- **Integrated bottom nav**: full-width bottom navigation with gold active indicator ## Color System @@ -27,7 +27,7 @@ Key aesthetics: | Card Surface | #242424 | Standard cards, product tiles | | Elevated Surface | #2E2E2E | Elevated cards, modal sheets, drawers | | Subtle Surface | #1F1F1F | Inset areas, grouped sections | -| Tab Bar Surface | #242424 | Bottom navigation pill fill | +| Tab Bar Surface | #242424 | Integrated bottom navigation surface | ### Text Colors diff --git a/crates/op-ai-skills/skills/style-guides/music-dark-mobile.md b/crates/op-ai-skills/skills/style-guides/music-dark-mobile.md index 91e645499..8080df245 100644 --- a/crates/op-ai-skills/skills/style-guides/music-dark-mobile.md +++ b/crates/op-ai-skills/skills/style-guides/music-dark-mobile.md @@ -15,7 +15,7 @@ Key aesthetics: - **Concert poster type**: Manrope at 700-800 weights for bold, punchy display headings - **Gradient energy**: Lime-to-cyan gradients on featured playlists and now-playing elements - **Audio-inspired radii**: 14-20px corners evoke the smooth, organic contours of speaker forms -- **Pill tab bar**: 100px radius bottom navigation with lime active indicator +- **Integrated bottom nav**: full-width bottom navigation with lime active indicator ## Color System @@ -28,7 +28,7 @@ Key aesthetics: | Elevated Surface | #1E1E1E | Now playing bar, modal sheets | | Gradient Start | #151515 | Gradient card dark edge | | Gradient End | #1A2A1A | Gradient card lime-tinted edge | -| Tab Bar Surface | #151515 | Bottom navigation pill fill | +| Tab Bar Surface | #151515 | Integrated bottom navigation surface | ### Text Colors diff --git a/crates/op-ai-skills/skills/style-guides/neon-purple-mobile-dark.md b/crates/op-ai-skills/skills/style-guides/neon-purple-mobile-dark.md index 249e79a46..72ce3bbc4 100644 --- a/crates/op-ai-skills/skills/style-guides/neon-purple-mobile-dark.md +++ b/crates/op-ai-skills/skills/style-guides/neon-purple-mobile-dark.md @@ -15,7 +15,7 @@ Key aesthetics: - **Geometric display type**: Space Grotesk at bold weights for confident, urban headings - **Glow depth**: Purple-tinted shadows and translucent surfaces add layers of visual depth - **Gradient cards**: Dark purple gradients on featured content for visual energy -- **Pill tab bar**: 100px radius bottom navigation with purple active glow +- **Integrated bottom nav**: full-width bottom navigation with purple active glow ## Color System @@ -28,7 +28,7 @@ Key aesthetics: | Elevated Surface | #221340 | Elevated cards, modal sheets | | Gradient Start | #1A0D2E | Gradient card dark edge | | Gradient End | #2D1B4E | Gradient card lighter edge | -| Tab Bar Surface | #1A0D2E | Bottom navigation pill fill | +| Tab Bar Surface | #1A0D2E | Integrated bottom navigation surface | ### Text Colors diff --git a/crates/op-ai-skills/skills/style-guides/pastel-soft-mobile-light.md b/crates/op-ai-skills/skills/style-guides/pastel-soft-mobile-light.md index ab4c33edc..63f8bbe61 100644 --- a/crates/op-ai-skills/skills/style-guides/pastel-soft-mobile-light.md +++ b/crates/op-ai-skills/skills/style-guides/pastel-soft-mobile-light.md @@ -15,7 +15,7 @@ Key aesthetics: - **Rounded typography**: Quicksand at all levels, its rounded letterforms echo the soft corner radii - **Pillow corners**: 16-24px radii make every card and container feel soft and touchable - **Pastel palette**: Muted pinks, lavenders, and warm grays for a cohesive dreamy feel -- **Pill tab bar**: 100px radius bottom navigation with pink active indicator +- **Integrated bottom nav**: full-width bottom navigation with pink active indicator ## Color System @@ -27,7 +27,7 @@ Key aesthetics: | Card Surface | #FFFFFF | Cards, elevated containers | | Inset Surface | #FFF0F3 | Input backgrounds, search bars, recessed areas | | Soft Surface | #FCE7F3 | Highlighted sections, feature callouts | -| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill | +| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface | ### Text Colors diff --git a/crates/op-ai-skills/skills/style-guides/social-vibrant-mobile-light.md b/crates/op-ai-skills/skills/style-guides/social-vibrant-mobile-light.md index 7cd71bb98..1967984e1 100644 --- a/crates/op-ai-skills/skills/style-guides/social-vibrant-mobile-light.md +++ b/crates/op-ai-skills/skills/style-guides/social-vibrant-mobile-light.md @@ -15,7 +15,7 @@ Key aesthetics: - **Friendly geometry**: Poppins typeface has naturally rounded letterforms that feel approachable and modern - **Generous radii**: 16-24px corners create soft, pillowy containers that invite touch interaction - **Gradient buttons**: Violet-to-pink gradients on primary actions create visual magnetism -- **Pill tab bar**: 100px radius bottom navigation with violet active indicator +- **Integrated bottom nav**: full-width bottom navigation with violet active indicator ## Color System @@ -27,7 +27,7 @@ Key aesthetics: | Card Surface | #FFFFFF | Cards, post containers | | Inset Surface | #F5F3FF | Input backgrounds, search bars, recessed areas | | Subtle Surface | #FDF2F8 | Story highlights, featured sections (pink tint) | -| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill | +| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface | ### Text Colors diff --git a/crates/op-ai-skills/skills/style-guides/terminal-minimal-dark.md b/crates/op-ai-skills/skills/style-guides/terminal-minimal-dark.md index 943d97c26..d127db237 100644 --- a/crates/op-ai-skills/skills/style-guides/terminal-minimal-dark.md +++ b/crates/op-ai-skills/skills/style-guides/terminal-minimal-dark.md @@ -15,7 +15,7 @@ Key aesthetics: - **Compact density**: Tight spacing and small radii (3-12px) keep information dense without clutter - **Glow indicators**: Status dots and active states use subtle cyan glow effects (blur 8, 40% opacity) - **Minimal chrome**: Borders are thin (#1E293B) and surfaces barely separate from the background -- **Pill tab bar**: Bottom navigation uses a 100px radius pill shape with icon+label layout +- **Integrated bottom nav**: bottom navigation uses compact icon+label items inside the page flow ## Color System @@ -26,7 +26,7 @@ Key aesthetics: | Page Background | #0A0F1C | Root screen background | | Card Surface | #111827 | Cards, input fields, list items | | Inset Surface | #0F172A | Nested containers, code blocks | -| Tab Bar Surface | #111827 | Bottom navigation pill background | +| Tab Bar Surface | #111827 | Integrated bottom navigation surface | ### Text Colors @@ -149,7 +149,7 @@ Key aesthetics: | 12px | Large cards, modal sheets | Comfortable but compact | | 100px | Tab bar pill, toggle pills | Full capsule shape | -Design rationale: Compact radii (3-12px) maintain a crisp, technical aesthetic. The sharp corners suggest precision engineering, while the slight rounding prevents a harsh brutalist feel. The 100px pill for the tab bar provides a strong contrast and clear affordance for the primary navigation element. +Design rationale: Compact radii (3-12px) maintain a crisp, technical aesthetic. The sharp corners suggest precision engineering, while the slight rounding prevents a harsh brutalist feel. The bottom navigation should stay compact and integrated, with clear affordance from icon/label contrast rather than a detached pill surface. ## Icons diff --git a/crates/op-ai-skills/skills/style-guides/travel-warm-mobile-light.md b/crates/op-ai-skills/skills/style-guides/travel-warm-mobile-light.md index 4bace0393..d33f4931a 100644 --- a/crates/op-ai-skills/skills/style-guides/travel-warm-mobile-light.md +++ b/crates/op-ai-skills/skills/style-guides/travel-warm-mobile-light.md @@ -15,7 +15,7 @@ Key aesthetics: - **Travel-journal type**: Outfit typeface balances geometric clarity with warm, approachable character - **Postcard radii**: 16-20px corners create soft, photo-friendly containers for destination imagery - **Earth-toned palette**: Warm grays and amber tints reinforce the organic, exploratory mood -- **Pill tab bar**: 100px radius bottom navigation with orange active indicator +- **Integrated bottom nav**: full-width bottom navigation with orange active indicator ## Color System @@ -27,7 +27,7 @@ Key aesthetics: | Card Surface | #FFFFFF | Cards, destination tiles, booking containers | | Inset Surface | #FFF7ED | Input backgrounds, search bars, recessed areas | | Subtle Surface | #FFFBEB | Featured destinations, promotional banners | -| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill | +| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface | ### Text Colors diff --git a/crates/op-ai-skills/skills/style-guides/warm-food-mobile-light.md b/crates/op-ai-skills/skills/style-guides/warm-food-mobile-light.md index dcb74a4a4..6b3738928 100644 --- a/crates/op-ai-skills/skills/style-guides/warm-food-mobile-light.md +++ b/crates/op-ai-skills/skills/style-guides/warm-food-mobile-light.md @@ -15,7 +15,7 @@ Key aesthetics: - **Rounded sans-serif**: Plus Jakarta Sans with rounded terminals echoes the soft, friendly radii - **Generous radii**: 16-20px corners make cards feel like warm, inviting menu tiles - **Food photography friendly**: Clean white card surfaces maximize contrast for food imagery -- **Pill tab bar**: 100px radius bottom navigation with orange active indicator +- **Integrated bottom nav**: full-width bottom navigation with orange active indicator ## Color System @@ -27,7 +27,7 @@ Key aesthetics: | Card Surface | #FFFFFF | Cards, menu items, elevated containers | | Inset Surface | #FFF1E3 | Input backgrounds, search bars, recessed areas | | Accent Surface | #FED7AA | Highlighted sections, promo banners, deal cards | -| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill | +| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface | ### Text Colors diff --git a/crates/op-ai-skills/skills/style-guides/wellness-green-mobile-light.md b/crates/op-ai-skills/skills/style-guides/wellness-green-mobile-light.md index 308f24c03..b73abff4d 100644 --- a/crates/op-ai-skills/skills/style-guides/wellness-green-mobile-light.md +++ b/crates/op-ai-skills/skills/style-guides/wellness-green-mobile-light.md @@ -15,7 +15,7 @@ Key aesthetics: - **Humanist geometry**: Outfit typeface balances geometric clarity with warm, approachable letterforms - **Organic radii**: 12-20px corners feel natural and comfortable, like smooth river stones - **Earth tones**: Warm grays and muted greens for secondary text and borders -- **Pill tab bar**: 100px radius bottom navigation with green active indicator +- **Integrated bottom nav**: full-width bottom navigation with green active indicator ## Color System @@ -27,7 +27,7 @@ Key aesthetics: | Card Surface | #FFFFFF | Cards, elevated containers | | Inset Surface | #F0F9EC | Input backgrounds, search bars, recessed areas | | Accent Surface | #DCFCE7 | Highlighted sections, progress indicators | -| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill | +| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface | ### Text Colors diff --git a/crates/op-ai-skills/src/loader.rs b/crates/op-ai-skills/src/loader.rs index 9972a5e46..aa2151a27 100644 --- a/crates/op-ai-skills/src/loader.rs +++ b/crates/op-ai-skills/src/loader.rs @@ -125,6 +125,39 @@ mod tests { ); } + #[test] + fn mobile_skill_carries_non_template_nav_and_spacing_rules() { + let skill = get_skill_by_name("mobile-app").expect("mobile-app skill present"); + assert!(skill.content.contains("Mobile top rhythm")); + assert!(skill.content.contains("Do not force bottom navigation")); + assert!(skill.content.contains("Not a floating pill")); + assert!(skill + .content + .contains("Do not repeat the same predictable mobile stack")); + assert!( + !skill.content.contains("BOTTOM TAB BAR — PILL STYLE"), + "mobile skill must not force the old pill-tab template" + ); + } + + #[test] + fn mobile_style_guides_do_not_force_pill_nav_template() { + for guide in crate::style_guide::style_guide_registry() { + if guide.platform == crate::style_guide::Platform::Mobile { + assert!( + !guide.content.contains("Pill tab bar"), + "{} must not force the old pill tab bar template", + guide.name + ); + assert!( + !guide.content.contains("bottom navigation pill"), + "{} must not force a detached pill nav", + guide.name + ); + } + } + } + #[test] fn block_list_and_next_line_array_triggers_load_non_empty() { // role-definitions / copywriting open the keyword array on the diff --git a/crates/op-host-desktop/src/chat_acp.rs b/crates/op-host-desktop/src/chat_acp.rs index 9c61360c0..22e377c1d 100644 --- a/crates/op-host-desktop/src/chat_acp.rs +++ b/crates/op-host-desktop/src/chat_acp.rs @@ -77,10 +77,16 @@ Always follow this three-phase pipeline (it produces higher quality than ad-hoc 1. **Load the design guide (ONCE)**: Call `get_design_prompt` to receive OpenPencil's design principles, node schema details, role system, color/typography tokens, and layout patterns. Read it carefully — it defines the canonical shapes and defaults. 2. **Build skeleton**: Call `design_skeleton` with a high-level description. This creates the structural frames (sections, layout containers) with correct auto-layout. 3. **Fill content**: Call `design_content` once per section from step 2, adding the concrete children (buttons, inputs, text, icons). -4. **Refine** (optional): Call `design_refine` on the root to apply final polish (consistent spacing, role-based styling). +4. **Refine**: ALWAYS call `design_refine` on the root after all sections are populated. This is mandatory for AI chat output because it applies final polish, consistent spacing, role-based styling, icon cleanup, and layout safety. Only fall back to `batch_design` or `insert_node` when the user explicitly asks for small/surgical edits rather than a new page. +## AI Chat Design Quality +- Mobile top rhythm: keep the status/header, title, and primary module close. On 375-430px screens, the gap from the header/title group to the first primary module (search, hero action, chart, or card) should usually be 20-32px, never a large empty band unless the request explicitly asks for a dramatic editorial hero. +- Product/card favorite/heart controls are functional `icon-button`s, not decorative badges. Place each favorite/heart fully inside its product card or product image with an 8-12px inset. Never use negative x/y, never straddle a card border, and never let the circle protrude into the section heading gap. +- Every new design needs a distinct visual concept before building: choose one concrete direction (for example editorial food magazine, glassy premium delivery, neo-brutal marketplace, calm bento dashboard, luxury concierge). Do not repeat the same predictable mobile stack of search + categories + orange promo + two cards unless that is genuinely the best fit for the prompt. +- Include one signature moment that gives the screen character: a crafted hero composition, editorial image treatment, distinctive category rail, playful but controlled card system, or refined data/offer module. Keep it purposeful and avoid decoration spam. + ## Modifying Existing Designs - Call `snapshot_layout` first to see the current tree. - Use `update_node` for property changes, `move_node` for reparenting, `delete_node` to remove. @@ -405,8 +411,10 @@ mod tests { )); assert!(ACP_SYSTEM_PROMPT.contains("- DO use the `mcp__openpencil__*` tools")); assert!(ACP_SYSTEM_PROMPT.contains("2. **Build skeleton**: Call `design_skeleton`")); + assert!(ACP_SYSTEM_PROMPT.contains("4. **Refine**: ALWAYS call `design_refine`")); + assert!(ACP_SYSTEM_PROMPT.contains("Mobile top rhythm")); + assert!(ACP_SYSTEM_PROMPT.contains("favorite/heart")); + assert!(ACP_SYSTEM_PROMPT.contains("signature moment")); assert!(ACP_SYSTEM_PROMPT.ends_with("Every key has a value; every value has a key.")); - // join('\n') line count: 44 array entries → 44 lines. - assert_eq!(ACP_SYSTEM_PROMPT.lines().count(), 44); } } diff --git a/crates/op-host-services/src/chat_system_prompt.rs b/crates/op-host-services/src/chat_system_prompt.rs index 9a9ad3fe0..63eb0af56 100644 --- a/crates/op-host-services/src/chat_system_prompt.rs +++ b/crates/op-host-services/src/chat_system_prompt.rs @@ -31,6 +31,14 @@ NEVER use "OpenPencil", "Pencil", or the tool name as brand/app name in designs. You may include 1-2 brief tags before the JSON (optional, keep them SHORT). When a user asks non-design questions (explain, suggest colors, give advice), respond in text."#; +const AI_CHAT_DESIGN_QUALITY: &str = r#"AI CHAT DESIGN QUALITY: +- Mobile top rhythm: keep the header/title group close to the first useful control or content module. On 375-430px screens, the gap from title/header to search, primary action, chart, or first card should usually be 20-32px. +- Search bars should be a single compact control row. Avoid a large tinted rounded shell containing another rounded input unless the user explicitly asks for that visual. +- Product card favorite/heart controls are functional icon-buttons. Keep every favorite/heart fully inside its card or image with an 8-12px inset; do not straddle card borders or float into section headings. +- Do not repeat the same predictable mobile stack of search + categories + orange promo + two cards. Pick a distinct visual concept for the domain before composing the screen. +- Give the first viewport one signature moment: a crafted hero/product composition, editorial crop, distinctive category rail, refined data module, or other domain-specific focal idea. +- Bottom navigation is optional and should be integrated with the page flow, not a detached floating pill or extra footer band."#; + /// TS `AGENT_TOOL_INSTRUCTIONS_CRUD` — verbatim port. The system /// prompt for tool-executing chat turns (builtin agent loop). const AGENT_TOOL_INSTRUCTIONS_CRUD: &str = r##"You are a design editor. Use tools to inspect, modify, insert, and delete elements on the canvas. @@ -89,7 +97,7 @@ pub fn build_chat_system_prompt(state: &EditorState, user_message: &str) -> Stri .map(|s| s.content.as_str()) .collect::>() .join("\n\n"); - format!("{CHAT_CORE_PROMPT}\n\n{knowledge}") + format!("{CHAT_CORE_PROMPT}\n\n{AI_CHAT_DESIGN_QUALITY}\n\n{knowledge}") } /// Build the agent-mode system prompt for a tool-executing builtin @@ -241,6 +249,17 @@ mod tests { assert!(prompt.len() > CHAT_CORE_PROMPT.len() + 100); } + #[test] + fn chat_system_prompt_carries_ai_chat_quality_guardrails() { + let state = EditorState::new(); + let prompt = build_chat_system_prompt(&state, "设计一个精致的美食应用移动端首页"); + assert!(prompt.contains("AI CHAT DESIGN QUALITY")); + assert!(prompt.contains("Mobile top rhythm")); + assert!(prompt.contains("favorite/heart")); + assert!(prompt.contains("Do not repeat the same predictable mobile stack")); + assert!(prompt.contains("signature moment")); + } + #[test] fn chat_system_prompt_includes_design_md_policy_when_present() { let mut state = EditorState::new(); diff --git a/crates/op-mcp/src/batch_layered.rs b/crates/op-mcp/src/batch_layered.rs index 63f26939f..c9876376a 100644 --- a/crates/op-mcp/src/batch_layered.rs +++ b/crates/op-mcp/src/batch_layered.rs @@ -380,6 +380,28 @@ chars. Content width: {}px.", ); } + if name.contains("search") || name.contains("搜索") { + return ( + format!( + "Create a single compact search row, not a hero panel: one search-bar/form-input \ +with search icon and placeholder plus an optional filter icon-button. Mobile height 48-56px, \ +gap=10-12, no oversized tinted shell, no nested rounded background inside another large rounded \ +background. Mobile top rhythm: place this primary module within 20-32px of the header/title group. \ +Content width: {}px.", + content_width + ), + vec![ + "row", + "search-bar", + "form-input", + "input", + "icon-button", + "icon", + "caption", + ], + ); + } + if name.contains("feature") || name.contains("功能") { return ( format!( @@ -620,7 +642,7 @@ fn default_root_gap(canvas_width: i32) -> i32 { fn quality_guidelines(canvas_width: i32) -> &'static str { if canvas_width <= 500 { - "Avoid crowded output: keep one primary mobile task, use an App Content wrapper with 16-20px horizontal padding and 20-24px vertical gaps, and limit above-the-fold modules." + "Avoid crowded output: keep one primary mobile task, use an App Content wrapper with 16-20px horizontal padding and 20-24px vertical gaps, and limit above-the-fold modules. Mobile top rhythm: keep the first primary module within 20-32px of the header/title group. Prefer a single compact search row with no oversized tinted shell. Add one signature moment and vary the composition instead of repeating a fixed template." } else { "Avoid crowded output: use generous section padding, clear type hierarchy, consistent card rhythm, and fewer stronger modules." } diff --git a/crates/op-mcp/src/batch_layered_tests.rs b/crates/op-mcp/src/batch_layered_tests.rs index 98ffde665..8ad808045 100644 --- a/crates/op-mcp/src/batch_layered_tests.rs +++ b/crates/op-mcp/src/batch_layered_tests.rs @@ -211,9 +211,14 @@ fn design_skeleton_mobile_root_defaults_to_breathing_room() { args.insert("canvasWidth".into(), "375".into()); match tool.call(&args) { - ToolOutcome::OkWithCommand(_, EditorCommand::InsertAuthoredSubtree { nodes, .. }) => { + ToolOutcome::OkWithCommand(out, EditorCommand::InsertAuthoredSubtree { nodes, .. }) => { let root = serde_json::to_value(&nodes[0]).expect("root json"); assert_eq!(root.get("gap").and_then(|v| v.as_f64()), Some(20.0)); + let sections = out.get("sections").expect("sections json"); + assert!(sections.contains("Mobile top rhythm")); + assert!(sections.contains("primary module within 20-32px")); + assert!(sections.contains("single compact search row")); + assert!(sections.contains("no oversized tinted shell")); } other => panic!("expected design_skeleton authored subtree, got {other:?}"), } diff --git a/crates/op-mcp/src/design_prompt.rs b/crates/op-mcp/src/design_prompt.rs index 8e7ccda63..349b716c8 100644 --- a/crates/op-mcp/src/design_prompt.rs +++ b/crates/op-mcp/src/design_prompt.rs @@ -60,11 +60,15 @@ const PLANNING_GUIDE: &str = r#"DESIGN PLANNING: const AESTHETIC_QUALITY_GUIDE: &str = r#"AESTHETIC QUALITY BAR: - Avoid crowded output. Prefer fewer, stronger modules with visible negative space over filling every pixel. - Mobile screens should use one App Content wrapper for the main body. The wrapper owns horizontal padding (16-20px) and vertical gap (20-24px); inner sections should not each add competing gutters. +- Mobile top rhythm: keep the header/title group and first primary module close. On 375-430px screens, the gap from a greeting/title/header cluster to search, primary action, chart, or first card should usually be 20-32px. Do not leave a blank hero-sized band above the first useful control unless the request explicitly asks for an editorial hero. - Keep one primary job per screen. Above the fold, show the title/context, the primary action or search, and at most 2-3 supporting modules. - Use a clear type rhythm: one display/title size, one section heading size, one body/caption size. Avoid many near-identical bold text sizes. - Reuse one card radius, one card padding, and one shadow treatment within a screen. - Use at most two saturated colors. Let hierarchy come from spacing, contrast, and content scale, not decoration. -- Mobile tab bars are part of the page flow; do not add fake bottom spacers or overlays."#; +- Product card favorite/heart controls must stay fully inside its card or image with an 8-12px inset. Treat favorite/heart as a functional icon-button, not a floating badge; never use negative x/y, straddle the card edge, or let it overlap the section heading row. +- Mobile tab bars are part of the page flow; do not add fake bottom spacers or overlays. +- Pick a distinct visual concept for each new design and build around one signature moment. Do not repeat the same predictable mobile stack of search + categories + orange promo + two cards unless the prompt specifically calls for that pattern. +- Use the user's domain to vary composition, imagery, palette, rhythm, and interaction affordances; polish should feel intentional rather than template-derived."#; const RUST_ELEMENT_TOOL_GUIDE: &str = r##"RUST MCP ELEMENT TOOL COMPATIBILITY: - This Rust MCP server does not expose the TS `add_*_v1` element-tool family unless those exact tools appear in tools/list. Do not call `add_*` tools just because older prompt text or examples mention them. diff --git a/crates/op-mcp/src/design_prompt_tests.rs b/crates/op-mcp/src/design_prompt_tests.rs index 3ef9d978f..bbccc08a2 100644 --- a/crates/op-mcp/src/design_prompt_tests.rs +++ b/crates/op-mcp/src/design_prompt_tests.rs @@ -48,6 +48,11 @@ fn get_design_prompt_full_includes_mobile_quality_guardrails() { assert!(prompt.contains("MOBILE APP")); assert!(prompt.contains("App Content wrapper")); assert!(prompt.contains("Avoid crowded output")); + assert!(prompt.contains("Mobile top rhythm")); + assert!(prompt.contains("favorite/heart")); + assert!(prompt.contains("must stay fully inside its card")); + assert!(prompt.contains("signature moment")); + assert!(prompt.contains("Do not repeat the same predictable mobile stack")); } other => panic!("expected prompt ok, got {other:?}"), } diff --git a/crates/op-orchestrator/src/compact_prompt.rs b/crates/op-orchestrator/src/compact_prompt.rs index c4688350b..caecbd430 100644 --- a/crates/op-orchestrator/src/compact_prompt.rs +++ b/crates/op-orchestrator/src/compact_prompt.rs @@ -133,6 +133,8 @@ pub fn build_compact_planning_prompt( .to_string(), "Plan one WOW FACTOR that is specific to the requested product/domain; avoid generic tinted wrappers, heavy shadows, or repeated rounded boxes as the main visual idea." .to_string(), + "Do not plan the same predictable mobile stack of search + categories + orange promo + two cards. Keep mobile top rhythm tight: no huge empty band between header/title and first useful module." + .to_string(), style_rule, ]; for r in &mobile_rules { @@ -179,6 +181,8 @@ mod tests { assert!(cp.system.contains("Create 2-4 cohesive subtasks")); assert!(cp.system.contains("SIGNATURE MOMENT")); assert!(cp.system.contains("WOW FACTOR")); + assert!(cp.system.contains("predictable mobile stack")); + assert!(cp.system.contains("mobile top rhythm tight")); assert_eq!(cp.user_prompt, "a mobile login screen"); } diff --git a/crates/op-orchestrator/src/prompt.rs b/crates/op-orchestrator/src/prompt.rs index 9bc68a901..493b3e962 100644 --- a/crates/op-orchestrator/src/prompt.rs +++ b/crates/op-orchestrator/src/prompt.rs @@ -69,6 +69,15 @@ Violating this format will cause a system error."; const MINIMAL_SUFFIX: &str = "\n\nOUTPUT ONLY ONE JSON OBJECT. No prose. No markdown. No tool calls."; +const PLANNING_QUALITY_GUARDRAILS: &str = r#" + +PLANNING QUALITY GUARDRAILS: +- Do not plan the same predictable mobile stack of search + categories + orange promo + two cards unless the request explicitly asks for that exact convention. +- Mobile top rhythm: planned header/title/search/primary-content sections should be compact; avoid allocating a huge empty band between the title and first useful module. +- Plan one signature moment in the first viewport: a crafted hero/product composition, editorial crop, distinctive category rail, refined data module, or other domain-specific focal idea. +- Bottom navigation is optional. If planned, it should be integrated with the page flow, not a detached floating pill, nested rounded capsule, or extra footer band. +- Product-card favorite/heart controls must be inside their card/image; never plan them as protruding decorative badges."#; + fn planning_suffix(mode: PlanningMode) -> &'static str { match mode { PlanningMode::Rich => RICH_SUFFIX, @@ -153,6 +162,7 @@ pub fn build_orchestrator_prompt( .map(|s| s.content.as_str()) .collect::>() .join("\n\n"); + system_prompt.push_str(PLANNING_QUALITY_GUARDRAILS); system_prompt.push_str(planning_suffix(mode)); PlanningPrompt { call_request: CallRequest { @@ -591,6 +601,9 @@ CRITICAL LAYOUT CONSTRAINTS:\n\ user_prompt.push_str( "\nMOBILE VERTICAL RHYTHM: Keep every section root height=\"fit_content\" and do not insert blank spacer frames or empty bands to fill the planned region. Header-to-search spacing should be 12-16px, search-to-next-section 16-24px, major module gaps 24-32px, and internal gaps usually 8/12/16px.", ); + user_prompt.push_str( + "\nMOBILE TOP RHYTHM: Keep the title/header group close to the first useful module. Do not leave a large blank band above search, categories, promo, charts, or first cards; if you need breathing room, use 20-32px, not a hero-sized void.", + ); user_prompt.push_str( "\nMOBILE SEARCH BAR: If generating search, output exactly one search control surface: 48-52px tall, width=\"fill_container\" inside the content rail, neutral/surface fill, subtle 1px border, cornerRadius 14-18, and a 18-20px search icon. Optional filter/sliders is a separate 44-48px square button beside it. Do not nest an input inside another rounded pill, do not use pink/tinted fills, and do not make the search section itself a huge rounded band.", ); @@ -618,6 +631,9 @@ CRITICAL LAYOUT CONSTRAINTS:\n\ user_prompt.push_str( "\nMOBILE NAV SHADOW: Do not add a drop shadow, glow, or detached shadow band behind the bottom navigation. If separation is needed, use a quiet 1px divider or subtle tonal difference that belongs to the page palette.", ); + user_prompt.push_str( + "\nNO FIXED FOOD TEMPLATE: Do not default to the same search + categories + orange promo + two product cards composition. For food, shopping, travel, fitness, finance, and social apps, choose a domain-specific visual concept and vary the first viewport composition.", + ); } // Port of orchestrator-sub-agent.ts:739-748 — APPEND MODE prompt injection. diff --git a/crates/op-orchestrator/src/prompt_tests.rs b/crates/op-orchestrator/src/prompt_tests.rs index a0a1497fd..38412b0c6 100644 --- a/crates/op-orchestrator/src/prompt_tests.rs +++ b/crates/op-orchestrator/src/prompt_tests.rs @@ -50,6 +50,16 @@ fn rich_prompt_has_style_guides_and_suffix() { assert_eq!(pp.call_request.user_prompt, req().prompt); } +#[test] +fn provider_planning_prompt_carries_quality_guardrails() { + let pp = build_orchestrator_prompt(&req(), PlanningMode::Rich, AbortFlag::new()); + let prompt = &pp.call_request.system_prompt; + assert!(prompt.contains("PLANNING QUALITY GUARDRAILS")); + assert!(prompt.contains("Do not plan the same predictable mobile stack")); + assert!(prompt.contains("Mobile top rhythm")); + assert!(prompt.contains("signature moment")); +} + #[test] fn minimal_prompt_has_short_suffix_no_snippets() { let pp = build_orchestrator_prompt(&req(), PlanningMode::Minimal, AbortFlag::new()); @@ -143,7 +153,7 @@ fn subagent_prompt_carries_ts_layout_contract() { false, ); - let required = "Page sections:|Food Categories [category chips]|Root frame: id=\"categories-root\"|width=\"fill_container\"|height=\"fit_content\"|Generate enough elements|MOBILE STATUS BAR|time, signal, wifi, battery|NO PHONE MOCKUP WRAPPER|MOBILE WIDTH SAFETY|MOBILE SINGLE CONTENT RAIL|MOBILE SEARCH BAR|MOBILE SECTION CHROME|MOBILE VERTICAL RHYTHM|MOBILE GRID ALIGNMENT|MOBILE CARD OVERLAYS|MOBILE IMAGE QUALITY|NO BLANK PLACEHOLDERS|MOBILE NAV SURFACE|MOBILE NAV SHADOW|TYPOGRAPHY HIERARCHY|DENSITY|VISUAL HIERARCHY|SPACING CONSISTENCY|CRAFT POLISH|MEDIA CONSISTENCY|ICON SCALE|SIGNATURE MOMENT|WOW FACTOR|COMPOSITIONAL CONTRAST|PREMIUM DETAIL|NO DECORATION SPAM"; + let required = "Page sections:|Food Categories [category chips]|Root frame: id=\"categories-root\"|width=\"fill_container\"|height=\"fit_content\"|Generate enough elements|MOBILE STATUS BAR|time, signal, wifi, battery|NO PHONE MOCKUP WRAPPER|MOBILE WIDTH SAFETY|MOBILE SINGLE CONTENT RAIL|MOBILE SEARCH BAR|MOBILE SECTION CHROME|MOBILE VERTICAL RHYTHM|MOBILE TOP RHYTHM|MOBILE GRID ALIGNMENT|MOBILE CARD OVERLAYS|MOBILE IMAGE QUALITY|NO BLANK PLACEHOLDERS|MOBILE NAV SURFACE|MOBILE NAV SHADOW|NO FIXED FOOD TEMPLATE|Do not default to the same search + categories + orange promo + two product cards composition|TYPOGRAPHY HIERARCHY|DENSITY|VISUAL HIERARCHY|SPACING CONSISTENCY|CRAFT POLISH|MEDIA CONSISTENCY|ICON SCALE|SIGNATURE MOMENT|WOW FACTOR|COMPOSITIONAL CONTRAST|PREMIUM DETAIL|NO DECORATION SPAM"; for required in required.split('|') { assert!(cr.user_prompt.contains(required), "missing {required}"); }