Compare commits

..

1 Commits

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

230
docs/CONTRIBUTING.md

@ -0,0 +1,230 @@ @@ -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

6
src/App.vue

@ -9,8 +9,8 @@ import HelloWorld from './components/HelloWorld.vue' @@ -9,8 +9,8 @@ import HelloWorld from './components/HelloWorld.vue'
<HelloWorld msg="toonpets coming soon" />
<nav>
<RouterLink to="/">home</RouterLink>
<RouterLink to="/about">about</RouterLink>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header>
@ -86,4 +86,4 @@ nav a:first-of-type { @@ -86,4 +86,4 @@ nav a:first-of-type {
<!-- trigger -->
<!-- trigger -->
<!-- trigger distinct-->
<!-- trigger 3 -->
<!-- trigger 3 -->

40
src/components/TheWelcome.vue

@ -12,10 +12,10 @@ import SupportIcon from './icons/IconSupport.vue' @@ -12,10 +12,10 @@ import SupportIcon from './icons/IconSupport.vue'
<template #icon>
<DocumentationIcon />
</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
pet that lives inside a spherical enclosure on their device. the pet evolves based on what food
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
it eats, developing a unique personality shaped by how it's been cared for.
</WelcomeItem>
@ -23,11 +23,11 @@ import SupportIcon from './icons/IconSupport.vue' @@ -23,11 +23,11 @@ import SupportIcon from './icons/IconSupport.vue'
<template #icon>
<EcosystemIcon />
</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
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
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,
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.
</WelcomeItem>
@ -35,11 +35,11 @@ import SupportIcon from './icons/IconSupport.vue' @@ -35,11 +35,11 @@ import SupportIcon from './icons/IconSupport.vue'
<template #icon>
<ToolingIcon />
</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
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
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
changes the sphere's visual environment. No single player can access everything — working
together with others fills in the gaps.
</WelcomeItem>
@ -47,11 +47,11 @@ import SupportIcon from './icons/IconSupport.vue' @@ -47,11 +47,11 @@ import SupportIcon from './icons/IconSupport.vue'
<template #icon>
<CommunityIcon />
</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
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
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
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.
</WelcomeItem>
@ -59,11 +59,11 @@ import SupportIcon from './icons/IconSupport.vue' @@ -59,11 +59,11 @@ import SupportIcon from './icons/IconSupport.vue'
<template #icon>
<SupportIcon />
</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
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
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
collects everything. The game finds beauty in incompleteness, with lived-in spaces that feel
grown rather than constructed.
</WelcomeItem>
</template>
</template>

166
src/components/ui/BubbleContainer.vue

@ -1,166 +0,0 @@ @@ -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>

316
src/views/AboutView.vue

@ -1,315 +1,15 @@ @@ -1,315 +1,15 @@
<script setup lang="ts">
import BubbleContainer from '../components/ui/BubbleContainer.vue'
</script>
<template>
<div class="about">
<div class="about-content">
<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>
<h1>This is an about page</h1>
</div>
</template>
<style scoped>
.about {
min-height: 100vh;
padding: 2rem 0;
}
.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>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>
</style>

Loading…
Cancel
Save