Compare commits

..

1 Commits

Author SHA1 Message Date
Diego Vester 12bbaf3ae7 docs: add CONTRIBUTING.md with guidelines 2 months ago
  1. 230
      docs/CONTRIBUTING.md
  2. 4
      src/App.vue
  3. 38
      src/components/TheWelcome.vue
  4. 166
      src/components/ui/BubbleContainer.vue
  5. 314
      src/views/AboutView.vue

230
docs/CONTRIBUTING.md

@ -0,0 +1,230 @@
# AI Coding Guidelines — toonpets.corneruniverse.com
## Project Overview
**Purpose:** Marketing website for Toon Pets, a smartphone video game about caring for virtual pets that evolve based on food.
**Current Audience:** Business owners, advisors, and potential partners helping with strategy.
**Future Audience:** Players who will download the game.
**Primary Goals:**
1. Clearly explain the game concept
2. Communicate the unique value proposition (relationship-building, wabi-sabi aesthetic, evolution mechanics)
3. Build credibility for business conversations
4. Eventually: convert visitors to download/wishlist the game
---
## Design Direction
**Aesthetic:** Warm, playful, approachable—but not childish. The game draws inspiration from Bomberman 64: The Second Attack, Toontown Online, Pocket Pikachu, and Megaman Battle Network. The visual identity should feel nostalgic yet fresh.
**Wabi-Sabi Influence:** Embrace imperfection, warmth, and organic feel. Avoid overly polished corporate aesthetics.
**Color Palette:** Define in `/src/styles/variables.css`. Prefer warm, inviting colors.
**Typography:** Friendly but readable. Consider a playful heading font paired with a clean body font.
---
## Technical Standards
### Vue 3 Conventions
- Use Composition API with `<script setup>` syntax
- Single File Components (.vue)
- Scoped styles by default
- TypeScript optional but prop types required
### File Structure
```
src/
├── components/
│ ├── common/ # Reusable components (Button, Card, Section)
│ ├── layout/ # Header, Footer, Navigation
│ └── sections/ # Page sections (HeroSection, FeaturesSection)
├── content/
│ └── site-data.js # Editable content separate from components
├── styles/
│ ├── variables.css # CSS custom properties
│ └── global.css # Base styles, resets
├── assets/
│ └── images/
├── App.vue
└── main.js
```
### Component Guidelines
**Naming:**
- PascalCase for component files: `HeroSection.vue`
- Components should be self-contained with clear, single responsibilities
- Prefix common components: `BaseButton.vue`, `BaseCard.vue`
**Props:**
```vue
<script setup>
defineProps({
title: { type: String, required: true },
subtitle: { type: String, default: '' }
})
</script>
```
**Content Separation:**
Keep text content in `/src/content/site-data.js` so it can be updated without modifying component logic:
```javascript
// src/content/site-data.js
export const hero = {
title: "Toon Pets",
subtitle: "Nurture, Feed, Evolve",
description: "A virtual pet game where your choices shape who your companion becomes."
}
export const features = [
{ title: "Relationship-Focused", description: "..." },
{ title: "Evolution Through Food", description: "..." }
]
```
### Styling
**CSS Custom Properties (variables.css):**
```css
:root {
/* Colors */
--color-primary: #...;
--color-secondary: #...;
--color-background: #...;
--color-text: #...;
/* Typography */
--font-heading: '...', sans-serif;
--font-body: '...', sans-serif;
/* Spacing */
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 2rem;
--space-xl: 4rem;
/* Breakpoints (for reference) */
/* Mobile: < 640px */
/* Tablet: 640px - 1024px */
/* Desktop: > 1024px */
}
```
**Responsive Design:**
- Mobile-first approach
- Use CSS Grid and Flexbox for layouts
- Breakpoints: 640px (tablet), 1024px (desktop)
**Scoped Styles:**
```vue
<style scoped>
.hero {
padding: var(--space-xl) var(--space-md);
}
</style>
```
---
## Components to Build
### Layout
- `AppHeader.vue` — Logo, navigation
- `AppFooter.vue` — Links, copyright
### Sections
- `HeroSection.vue` — Main headline, tagline, primary CTA
- `ConceptSection.vue` — Explain what Toon Pets is
- `FeaturesSection.vue` — Key game features with icons/illustrations
- `EvolutionSection.vue` — Showcase the food-based evolution mechanic
- `MediaSection.vue` — Screenshots, GIFs, or video
- `SignupSection.vue` — Email capture for updates
### Common
- `BaseButton.vue` — Primary and secondary variants
- `BaseCard.vue` — Reusable card component
- `SectionWrapper.vue` — Consistent section padding and max-width
---
## Content Strategy
**Current Phase (Business/Strategy):**
- Emphasize the unique game design
- Highlight market positioning and influences
- Professional but personality-filled tone
**Future Phase (Player Marketing):**
- Focus on emotional appeal and fun
- Showcase gameplay and pet personalities
- Strong download/wishlist CTA
Consider a config flag to switch messaging phases:
```javascript
// src/content/site-data.js
export const sitePhase = 'business' // or 'player'
```
---
## Accessibility
- Semantic HTML: `<header>`, `<main>`, `<section>`, `<footer>`
- Heading hierarchy: One `<h1>` per page, logical `<h2>`, `<h3>` structure
- Alt text for all images
- Sufficient color contrast
- Keyboard-navigable interactive elements
---
## Performance
- Optimize images before adding to repo
- Lazy-load images below the fold
- Minimize dependencies—vanilla CSS over utility frameworks
- Use `v-once` for static content sections
---
## Git Workflow
**Branch Naming:**
- `feat/add-hero-section`
- `fix/mobile-navigation`
- `content/update-features-text`
**Commit Messages:**
- `feat: add hero section with CTA`
- `fix: correct spacing on mobile`
- `content: update game description`
**Pull Requests:**
- Keep PRs focused on single features or fixes
- Preview builds are automatic—review before merging
---
## Do's and Don'ts
**Do:**
- Keep components small and focused
- Separate content from presentation
- Test on mobile viewport sizes
- Use CSS custom properties for consistency
- Write descriptive alt text
**Don't:**
- Hardcode text content in components
- Use inline styles
- Add unnecessary dependencies
- Forget mobile responsiveness
- Over-engineer—start simple

4
src/App.vue

@ -9,8 +9,8 @@ import HelloWorld from './components/HelloWorld.vue'
<HelloWorld msg="toonpets coming soon" /> <HelloWorld msg="toonpets coming soon" />
<nav> <nav>
<RouterLink to="/">home</RouterLink> <RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">about</RouterLink> <RouterLink to="/about">About</RouterLink>
</nav> </nav>
</div> </div>
</header> </header>

38
src/components/TheWelcome.vue

@ -12,10 +12,10 @@ import SupportIcon from './icons/IconSupport.vue'
<template #icon> <template #icon>
<DocumentationIcon /> <DocumentationIcon />
</template> </template>
<template #heading>the concept</template> <template #heading>The Concept</template>
toon pets is a virtual pet game for smartphones, built with godot 4. players care for a virtual Toon Pets is a virtual pet game for smartphones, built with Godot 4. Players care for a virtual
pet that lives inside a spherical enclosure on their device. the pet evolves based on what food pet that lives inside a spherical enclosure on their device. The pet evolves based on what food
it eats, developing a unique personality shaped by how it's been cared for. it eats, developing a unique personality shaped by how it's been cared for.
</WelcomeItem> </WelcomeItem>
@ -23,11 +23,11 @@ import SupportIcon from './icons/IconSupport.vue'
<template #icon> <template #icon>
<EcosystemIcon /> <EcosystemIcon />
</template> </template>
<template #heading>the sphere</template> <template #heading>The Sphere</template>
the game takes place inside a spherical enclosure the pet's private world contained within The game takes place inside a spherical enclosure the pet's private world contained within
your smartphone. the pet walks along the interior surface in a closed loop, creating a cozy, your smartphone. The pet walks along the interior surface in a closed loop, creating a cozy,
intimate space. the sphere's appearance changes based on the pet's evolution, and customization intimate space. The sphere's appearance changes based on the pet's evolution, and customization
emerges through partnership: the pet curates options, and you choose from what they offer. emerges through partnership: the pet curates options, and you choose from what they offer.
</WelcomeItem> </WelcomeItem>
@ -35,11 +35,11 @@ import SupportIcon from './icons/IconSupport.vue'
<template #icon> <template #icon>
<ToolingIcon /> <ToolingIcon />
</template> </template>
<template #heading>evolution</template> <template #heading>Evolution</template>
feed your pet meat, fruit, or sweets to guide their evolution across three stages, resulting in Feed your pet meat, fruit, or sweets to guide their evolution across three stages, resulting in
17 possible forms. each evolution unlocks new weapon types for combat (up to 4 of 6 total) and 17 possible forms. Each evolution unlocks new weapon types for combat (up to 4 of 6 total) and
changes the sphere's visual environment. no single player can access everything working changes the sphere's visual environment. No single player can access everything working
together with others fills in the gaps. together with others fills in the gaps.
</WelcomeItem> </WelcomeItem>
@ -47,11 +47,11 @@ import SupportIcon from './icons/IconSupport.vue'
<template #icon> <template #icon>
<CommunityIcon /> <CommunityIcon />
</template> </template>
<template #heading>multiplayer & social</template> <template #heading>Multiplayer & Social</template>
connect with nearby players automatically to battle enemies together in turn-based combat. link Connect with nearby players automatically to battle enemies together in turn-based combat. Link
with friends so your pets remember each other across sessions. send your pet to visit a friend's with friends so your pets remember each other across sessions. Send your pet to visit a friend's
sphere they'll return with stories, items, and a stronger bond. two pets can even co-habitate sphere they'll return with stories, items, and a stronger bond. Two pets can even co-habitate
temporarily, playing and napping together on your screen. temporarily, playing and napping together on your screen.
</WelcomeItem> </WelcomeItem>
@ -59,11 +59,11 @@ import SupportIcon from './icons/IconSupport.vue'
<template #icon> <template #icon>
<SupportIcon /> <SupportIcon />
</template> </template>
<template #heading>wabi-sabi philosophy</template> <template #heading>Wabi-Sabi Philosophy</template>
toon pets embraces imperfection and transience. your pet's "suboptimal" evolution path isn't Toon Pets embraces imperfection and transience. Your pet's "suboptimal" evolution path isn't
worse it's yours, shaped by your choices. visits end, moments are fleeting, and no player worse it's yours, shaped by your choices. Visits end, moments are fleeting, and no player
collects everything. the game finds beauty in incompleteness, with lived-in spaces that feel collects everything. The game finds beauty in incompleteness, with lived-in spaces that feel
grown rather than constructed. grown rather than constructed.
</WelcomeItem> </WelcomeItem>
</template> </template>

166
src/components/ui/BubbleContainer.vue

@ -1,166 +0,0 @@
<script setup lang="ts">
interface Props {
padding?: 'sm' | 'md' | 'lg' | 'xl'
variant?: 'default' | 'subtle' | 'prominent'
}
withDefaults(defineProps<Props>(), {
padding: 'md',
variant: 'default'
})
</script>
<template>
<div :class="['bubble-container', `bubble-container--${padding}`, `bubble-container--${variant}`]">
<slot />
</div>
</template>
<style scoped>
.bubble-container {
/* Base structure */
position: relative;
border-radius: 1.25rem;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
/* Light mode base */
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.25),
rgba(255, 255, 255, 0.08)
);
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
inset 0 -1px 0 rgba(0, 0, 0, 0.05);
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
.bubble-container {
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.12),
rgba(255, 255, 255, 0.03)
);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.15),
inset 0 -1px 0 rgba(0, 0, 0, 0.2);
}
}
/* Padding variants */
.bubble-container--sm {
padding: 0.75rem;
}
.bubble-container--md {
padding: 1.25rem;
}
.bubble-container--lg {
padding: 2rem;
}
.bubble-container--xl {
padding: 3rem;
}
/* Style variants */
.bubble-container--subtle {
/* Light mode subtle */
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.15),
rgba(255, 255, 255, 0.05)
);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow:
0 4px 16px rgba(0, 0, 0, 0.05),
inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
@media (prefers-color-scheme: dark) {
.bubble-container--subtle {
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.08),
rgba(255, 255, 255, 0.02)
);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow:
0 4px 16px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
}
.bubble-container--prominent {
/* Light mode prominent */
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.35),
rgba(255, 255, 255, 0.12)
);
border: 1px solid rgba(255, 255, 255, 0.4);
box-shadow:
0 12px 48px rgba(0, 0, 0, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.5),
inset 0 -1px 0 rgba(0, 0, 0, 0.08);
}
@media (prefers-color-scheme: dark) {
.bubble-container--prominent {
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.18),
rgba(255, 255, 255, 0.05)
);
border: 1px solid rgba(255, 255, 255, 0.25);
box-shadow:
0 12px 48px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.2),
inset 0 -1px 0 rgba(0, 0, 0, 0.3);
}
}
/* Hover effects */
.bubble-container {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.bubble-container:hover {
transform: translateY(-2px);
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.5),
inset 0 -1px 0 rgba(0, 0, 0, 0.08);
}
@media (prefers-color-scheme: dark) {
.bubble-container:hover {
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.2),
inset 0 -1px 0 rgba(0, 0, 0, 0.25);
}
}
/* Responsive adjustments */
@media (max-width: 768px) {
.bubble-container {
border-radius: 1rem;
}
.bubble-container--lg {
padding: 1.5rem;
}
.bubble-container--xl {
padding: 2rem;
}
}
</style>

314
src/views/AboutView.vue

@ -1,315 +1,15 @@
<script setup lang="ts">
import BubbleContainer from '../components/ui/BubbleContainer.vue'
</script>
<template> <template>
<div class="about"> <div class="about">
<div class="about-content"> <h1>This is an about page</h1>
<h1 class="page-title">Component Library</h1>
<p class="page-subtitle">Experimenting with reusable UI components</p>
<div class="component-showcase">
<section class="showcase-section">
<h2>Bubble Container</h2>
<p class="section-description">
A versatile container component with glassmorphism design, featuring transparency,
rounded borders, and automatic light/dark mode support.
</p>
<div class="examples-grid">
<div class="example">
<h3>Default</h3>
<BubbleContainer>
<h4>Standard Container</h4>
<p>This is the default bubble container with medium padding and standard transparency.</p>
<div class="demo-content">
<span class="demo-tag">Feature 1</span>
<span class="demo-tag">Feature 2</span>
</div>
</BubbleContainer>
</div>
<div class="example">
<h3>Subtle Variant</h3>
<BubbleContainer variant="subtle" padding="lg">
<h4>Subtle Style</h4>
<p>Less prominent with reduced opacity and softer shadows for background elements.</p>
<div class="demo-list">
<div class="demo-item"> Reduced opacity</div>
<div class="demo-item"> Softer shadows</div>
<div class="demo-item"> Large padding</div>
</div>
</BubbleContainer>
</div>
<div class="example">
<h3>Prominent Variant</h3>
<BubbleContainer variant="prominent" padding="xl">
<h4>Prominent Style</h4>
<p>Enhanced visibility with stronger borders and more pronounced shadows.</p>
<div class="demo-stats">
<div class="stat">
<div class="stat-number">95%</div>
<div class="stat-label">Transparency</div>
</div>
<div class="stat">
<div class="stat-number">10px</div>
<div class="stat-label">Blur Effect</div>
</div>
</div>
</BubbleContainer>
</div>
<div class="example">
<h3>Small Padding</h3>
<BubbleContainer padding="sm">
<h4>Compact Content</h4>
<p>Perfect for smaller UI elements and tight layouts.</p>
</BubbleContainer>
</div>
<div class="example full-width">
<h3>Nested Containers</h3>
<BubbleContainer padding="lg">
<h4>Parent Container</h4>
<p>Containers can be nested for complex layouts:</p>
<div class="nested-demo">
<BubbleContainer variant="subtle" padding="md">
<strong>Nested Child</strong>
<p>This creates interesting depth effects with multiple layers of transparency.</p>
</BubbleContainer>
</div>
</BubbleContainer>
</div>
</div>
</section>
<section class="showcase-section">
<BubbleContainer variant="prominent" padding="lg">
<h2>Usage Notes</h2>
<div class="usage-grid">
<div class="usage-item">
<h4>Responsive Design</h4>
<p>Automatically adjusts border radius and padding on mobile devices.</p>
</div>
<div class="usage-item">
<h4>Theme Support</h4>
<p>Seamlessly switches between light and dark modes using CSS media queries.</p>
</div>
<div class="usage-item">
<h4>Performance</h4>
<p>Uses backdrop-filter for efficient blur effects with hardware acceleration.</p>
</div>
<div class="usage-item">
<h4>Accessibility</h4>
<p>Maintains proper contrast ratios in both light and dark themes.</p>
</div>
</div>
</BubbleContainer>
</section>
</div>
</div>
</div> </div>
</template> </template>
<style scoped> <style>
.about { @media (min-width: 1024px) {
min-height: 100vh; .about {
padding: 2rem 0; min-height: 100vh;
} display: flex;
align-items: center;
.about-content {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
.page-title {
font-size: 3rem;
font-weight: 600;
text-align: center;
margin-bottom: 1rem;
background: linear-gradient(135deg, var(--color-heading), var(--color-text));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.page-subtitle {
font-size: 1.25rem;
text-align: center;
color: var(--color-text);
opacity: 0.8;
margin-bottom: 3rem;
}
.component-showcase {
display: flex;
flex-direction: column;
gap: 3rem;
}
.showcase-section {
display: flex;
flex-direction: column;
gap: 2rem;
}
.showcase-section > h2 {
font-size: 2rem;
font-weight: 500;
color: var(--color-heading);
margin-bottom: 0.5rem;
}
.section-description {
font-size: 1.1rem;
line-height: 1.6;
color: var(--color-text);
opacity: 0.9;
margin-bottom: 1rem;
}
.examples-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
align-items: start;
}
.example {
display: flex;
flex-direction: column;
gap: 1rem;
}
.example.full-width {
grid-column: 1 / -1;
}
.example > h3 {
font-size: 1.25rem;
font-weight: 500;
color: var(--color-heading);
margin-bottom: 0.5rem;
}
.example h4 {
font-size: 1.1rem;
font-weight: 500;
color: var(--color-heading);
margin-bottom: 0.75rem;
}
.example p {
line-height: 1.6;
margin-bottom: 1rem;
}
.demo-content {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.demo-tag {
background: rgba(var(--vt-c-indigo), 0.1);
color: var(--color-heading);
padding: 0.25rem 0.75rem;
border-radius: 1rem;
font-size: 0.875rem;
border: 1px solid rgba(var(--vt-c-indigo), 0.2);
}
.demo-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.demo-item {
font-size: 0.9rem;
opacity: 0.9;
}
.demo-stats {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-top: 1rem;
}
.stat {
text-align: center;
}
.stat-number {
font-size: 1.5rem;
font-weight: 600;
color: var(--color-heading);
}
.stat-label {
font-size: 0.875rem;
opacity: 0.8;
}
.nested-demo {
margin-top: 1rem;
}
.usage-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-top: 1rem;
}
.usage-item h4 {
font-size: 1rem;
font-weight: 500;
color: var(--color-heading);
margin-bottom: 0.5rem;
}
.usage-item p {
font-size: 0.9rem;
line-height: 1.5;
margin: 0;
opacity: 0.9;
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.about-content {
padding: 0 0.5rem;
}
.page-title {
font-size: 2.5rem;
}
.examples-grid {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.demo-stats {
grid-template-columns: 1fr;
}
.usage-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.page-title {
font-size: 2rem;
}
.page-subtitle {
font-size: 1.1rem;
} }
} }
</style> </style>
Loading…
Cancel
Save