From dca4d3d85685d996c7edc7d41f550b35971a2da5 Mon Sep 17 00:00:00 2001 From: Diego Vester Date: Sat, 24 Jan 2026 12:53:47 -0600 Subject: [PATCH] feat: change codebase structure --- docs/prompts/0.md | 12 ++ src/App.vue | 94 ++------- src/assets/base.css | 8 +- src/assets/main.css | 2 +- src/components/HelloWorld.vue | 7 +- src/components/TheWelcome.vue | 79 +++++--- src/components/WelcomeItem.vue | 4 +- src/components/__tests__/HelloWorld.spec.ts | 18 +- src/components/icons/IconCommunity.vue | 7 +- src/components/icons/IconDocumentation.vue | 7 +- src/components/icons/IconEcosystem.vue | 7 +- src/components/icons/IconSupport.vue | 7 +- src/components/layout/AppFooter.vue | 100 ++++++++++ src/components/layout/AppHeader.vue | 81 ++++++++ src/components/sections/HeroSection.vue | 64 +++++++ src/content/plushies.ts | 65 +++++++ src/content/site-data.ts | 200 ++++++++++++++++++++ src/main.ts | 18 +- src/router/index.ts | 18 +- src/stores/counter.ts | 16 +- src/styles/global.css | 118 ++++++++++++ src/styles/variables.css | 132 +++++++++++++ src/views/AboutView.vue | 58 +++++- src/views/HomeView.vue | 20 +- 24 files changed, 981 insertions(+), 161 deletions(-) create mode 100644 docs/prompts/0.md create mode 100644 src/components/layout/AppFooter.vue create mode 100644 src/components/layout/AppHeader.vue create mode 100644 src/components/sections/HeroSection.vue create mode 100644 src/content/plushies.ts create mode 100644 src/content/site-data.ts create mode 100644 src/styles/global.css create mode 100644 src/styles/variables.css diff --git a/docs/prompts/0.md b/docs/prompts/0.md new file mode 100644 index 0000000..d225632 --- /dev/null +++ b/docs/prompts/0.md @@ -0,0 +1,12 @@ +I am developing a website with Vue 3 called lukastitch.corneruniverse.com for my friend. You are a coding assistance helping me build this website. Please read the contribution guidelines indocs/CONTRIBUTING.md. The codebase currently is the template provided when initializing a new Vue 3 project. I would like to continue to take inspiration from the template, utilizingcomponents, templates, and the router. I'd like to minimize bloat in App.vue. + + +As a side note, I've create an AI code generating tool called agent.corneruniverse.com. I primarilycreated the tool for myself for convenience. I intend to use it from my mobile device when taking public transportation. I will also allow my friend to use this tool to make changes to the website. + + +My friend is not technical and is not a developer. My friend will use language when generating code. We should build the website with this in mind to be more compatible with casual lingo when someone uses the agent.corneruniverse.com ai coding tool. An example of being compatible with casual lingo would be my friend creating a prompt to “change the title” or “change the subtitle” of the website. From my friend’s perspective, the msg variable from App.vue containing the text “You Did It!” would be title. The text in HelloWorld.vue of “You’ve successfully created a project with [Vite](https://vite.dev/) + [Vue 3](https://vuejs.org/). What's next?” would be the subtitle. + +My friend will not have context of specifics in the codebase. My friend would not anticipate that the msg variable is a prop being passed into a component. The AI agent would not understand that a human may refer to the msg variable as a “title”. We should develop the codebase to be more “human compatible”. According to the CONTRIBUTING.md guidelines, site content should be stored in `/src/content/site-data.js`. One possible solution would be to add a field such as “tags” that contain syllables of what a human may use to refer to the content data. + + +My friend requested that the colors for the website be “Pastel! I love pastel colors. Pink, purple, and grey”. \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index 7905b05..d249a76 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,85 +1,27 @@ diff --git a/src/assets/base.css b/src/assets/base.css index 8816868..4d16fc2 100644 --- a/src/assets/base.css +++ b/src/assets/base.css @@ -70,14 +70,14 @@ body { Inter, -apple-system, BlinkMacSystemFont, - 'Segoe UI', + "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, - 'Fira Sans', - 'Droid Sans', - 'Helvetica Neue', + "Fira Sans", + "Droid Sans", + "Helvetica Neue", sans-serif; font-size: 15px; text-rendering: optimizeLegibility; diff --git a/src/assets/main.css b/src/assets/main.css index 36fb845..cdbdca2 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -1,4 +1,4 @@ -@import './base.css'; +@import "./base.css"; #app { max-width: 1280px; diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue index d174cf8..fe7b5bc 100644 --- a/src/components/HelloWorld.vue +++ b/src/components/HelloWorld.vue @@ -1,7 +1,7 @@ diff --git a/src/components/TheWelcome.vue b/src/components/TheWelcome.vue index 8b731d9..e936a05 100644 --- a/src/components/TheWelcome.vue +++ b/src/components/TheWelcome.vue @@ -1,12 +1,12 @@ - As an independent project, Vue relies on community backing for its sustainability. You can help - us by - becoming a sponsor. + As an independent project, Vue relies on community backing for its + sustainability. You can help us by + becoming a sponsor. diff --git a/src/components/WelcomeItem.vue b/src/components/WelcomeItem.vue index 6d7086a..d40a319 100644 --- a/src/components/WelcomeItem.vue +++ b/src/components/WelcomeItem.vue @@ -59,7 +59,7 @@ h3 { } .item:before { - content: ' '; + content: " "; border-left: 1px solid var(--color-border); position: absolute; left: 0; @@ -68,7 +68,7 @@ h3 { } .item:after { - content: ' '; + content: " "; border-left: 1px solid var(--color-border); position: absolute; left: 0; diff --git a/src/components/__tests__/HelloWorld.spec.ts b/src/components/__tests__/HelloWorld.spec.ts index 2533202..da11e2e 100644 --- a/src/components/__tests__/HelloWorld.spec.ts +++ b/src/components/__tests__/HelloWorld.spec.ts @@ -1,11 +1,11 @@ -import { describe, it, expect } from 'vitest' +import { describe, it, expect } from "vitest"; -import { mount } from '@vue/test-utils' -import HelloWorld from '../HelloWorld.vue' +import { mount } from "@vue/test-utils"; +import HelloWorld from "../HelloWorld.vue"; -describe('HelloWorld', () => { - it('renders properly', () => { - const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } }) - expect(wrapper.text()).toContain('Hello Vitest') - }) -}) +describe("HelloWorld", () => { + it("renders properly", () => { + const wrapper = mount(HelloWorld, { props: { msg: "Hello Vitest" } }); + expect(wrapper.text()).toContain("Hello Vitest"); + }); +}); diff --git a/src/components/icons/IconCommunity.vue b/src/components/icons/IconCommunity.vue index 2dc8b05..ea8ddef 100644 --- a/src/components/icons/IconCommunity.vue +++ b/src/components/icons/IconCommunity.vue @@ -1,5 +1,10 @@ + + diff --git a/src/components/layout/AppHeader.vue b/src/components/layout/AppHeader.vue new file mode 100644 index 0000000..a14065d --- /dev/null +++ b/src/components/layout/AppHeader.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/src/components/sections/HeroSection.vue b/src/components/sections/HeroSection.vue new file mode 100644 index 0000000..035c930 --- /dev/null +++ b/src/components/sections/HeroSection.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/src/content/plushies.ts b/src/content/plushies.ts new file mode 100644 index 0000000..1a7a221 --- /dev/null +++ b/src/content/plushies.ts @@ -0,0 +1,65 @@ +/** + * Plushie Catalog - lukastitch.corneruniverse.com + * + * AI CODING TOOL INSTRUCTIONS: + * This file contains the list of plushies displayed in the gallery. + * + * Common user requests and how to handle them: + * - "add a plushie" -> add a new object to the plushies array + * - "remove a plushie" -> remove the object with matching name/id + * - "mark as sold" -> set available: false + * - "mark as available" -> set available: true + * - "change plushie description" -> update the description field + * - "update etsy link" -> update the etsyLink field + */ + +export interface Plushie { + id: string; + name: string; + image: string; + description: string; + size: string; + available: boolean; + etsyLink: string | null; + tags?: string[]; // Optional: helps with search +} + +export const plushies: Plushie[] = [ + // Example entry - replace with real plushies: + // { + // id: 'cozy-bear', + // name: 'Cozy Bear', + // image: '/images/plushies/cozy-bear.jpg', + // description: 'A huggable friend for cold nights.', + // size: '12 inches', + // available: false, + // etsyLink: null, + // tags: ['bear', 'cozy', 'huggable'] + // } +]; + +/** + * Find a plushie by name (case-insensitive partial match) + */ +export function findPlushie(searchName: string): Plushie | undefined { + const normalized = searchName.toLowerCase().trim(); + return plushies.find( + (p) => + p.name.toLowerCase().includes(normalized) || + p.id.toLowerCase().includes(normalized), + ); +} + +/** + * Get available plushies only + */ +export function getAvailablePlushies(): Plushie[] { + return plushies.filter((p) => p.available); +} + +/** + * Get sold plushies only + */ +export function getSoldPlushies(): Plushie[] { + return plushies.filter((p) => !p.available); +} diff --git a/src/content/site-data.ts b/src/content/site-data.ts new file mode 100644 index 0000000..a237f7f --- /dev/null +++ b/src/content/site-data.ts @@ -0,0 +1,200 @@ +/** + * Site Content Data - lukastitch.corneruniverse.com + * + * This file contains all editable site content in one place. + * + * AI CODING TOOL INSTRUCTIONS: + * Each content field has a "tags" array containing words and phrases that + * humans might use to refer to this content. When a user says something like + * "change the title" or "update the main heading", match their language to + * the tags to find the correct field to modify. + * + * Tag matching examples: + * - "change the title" -> look for tags containing "title" + * - "update the subtitle" -> look for tags containing "subtitle" + * - "change the tagline" -> look for tags containing "tagline" + * - "update the description" -> look for tags containing "description" + */ + +interface ContentField { + value: string | null; + tags: string[]; +} + +interface HeroContent { + title: ContentField; + tagline: ContentField; + description: ContentField; +} + +interface AboutContent { + heading: ContentField; + text: ContentField; + image: ContentField; +} + +interface ContactContent { + heading: ContentField; + text: ContentField; + etsyLink: ContentField; + email: ContentField; +} + +export const hero: HeroContent = { + title: { + value: "Luka Stitch", + tags: [ + "title", + "main title", + "heading", + "main heading", + "name", + "brand name", + "site name", + "website name", + "big text", + "header text", + ], + }, + tagline: { + value: "Handmade plushies with love", + tags: [ + "tagline", + "subtitle", + "slogan", + "motto", + "subheading", + "secondary text", + "smaller text under title", + ], + }, + description: { + value: "Each creation is one-of-a-kind, crafted with care.", + tags: [ + "description", + "intro", + "introduction", + "about text", + "body text", + "paragraph", + "blurb", + ], + }, +}; + +export const about: AboutContent = { + heading: { + value: "About", + tags: ["about title", "about heading", "about section title"], + }, + text: { + value: + "Hi! I'm the maker behind Luka Stitch. I create handmade plushies, each one crafted with love and attention to detail. Every plushie is unique and made to bring joy.", + tags: [ + "about text", + "about description", + "about paragraph", + "bio", + "biography", + "about me", + "maker story", + ], + }, + image: { + value: "/images/about-photo.jpg", + tags: ["about image", "about photo", "maker photo", "profile photo"], + }, +}; + +export const contact: ContactContent = { + heading: { + value: "Get in Touch", + tags: ["contact title", "contact heading", "get in touch title"], + }, + text: { + value: "Interested in a custom plushie? I'd love to hear from you!", + tags: [ + "contact text", + "contact description", + "contact message", + "reach out text", + ], + }, + etsyLink: { + value: "https://etsy.com/shop/lukastitch", + tags: ["etsy", "etsy link", "shop link", "store link", "buy link"], + }, + email: { + value: null, + tags: ["email", "email address", "contact email"], + }, +}; + +type SectionName = "hero" | "about" | "contact"; + +interface TagSearchResult { + section: SectionName; + field: string; + value: string | null; + tags: string[]; +} + +/** + * Helper function to find content by tag + * Usage: findByTag("title") returns { section: "hero", field: "title", value: "Luka Stitch" } + */ +export function findByTag(searchTag: string): TagSearchResult | null { + const normalizedSearch = searchTag.toLowerCase().trim(); + const sections = { hero, about, contact } as const; + + for (const sectionName of Object.keys(sections) as SectionName[]) { + const section = sections[sectionName]; + for (const [fieldName, field] of Object.entries(section)) { + const contentField = field as ContentField; + if ( + contentField.tags && + contentField.tags.some( + (tag: string) => + tag.toLowerCase().includes(normalizedSearch) || + normalizedSearch.includes(tag.toLowerCase()), + ) + ) { + return { + section: sectionName, + field: fieldName, + value: contentField.value, + tags: contentField.tags, + }; + } + } + } + return null; +} + +interface ContentItem { + path: string; + value: string | null; + tags: string[]; +} + +/** + * Get all content as a flat list with their tags + * Useful for AI tools to understand available content + */ +export function getAllContent(): ContentItem[] { + const sections = { hero, about, contact } as const; + const content: ContentItem[] = []; + + for (const sectionName of Object.keys(sections) as SectionName[]) { + const section = sections[sectionName]; + for (const [fieldName, field] of Object.entries(section)) { + const contentField = field as ContentField; + content.push({ + path: `${sectionName}.${fieldName}`, + value: contentField.value, + tags: contentField.tags || [], + }); + } + } + return content; +} diff --git a/src/main.ts b/src/main.ts index 5dcad83..48e95d2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,14 +1,14 @@ -import './assets/main.css' +import "./styles/global.css"; -import { createApp } from 'vue' -import { createPinia } from 'pinia' +import { createApp } from "vue"; +import { createPinia } from "pinia"; -import App from './App.vue' -import router from './router' +import App from "./App.vue"; +import router from "./router"; -const app = createApp(App) +const app = createApp(App); -app.use(createPinia()) -app.use(router) +app.use(createPinia()); +app.use(router); -app.mount('#app') +app.mount("#app"); diff --git a/src/router/index.ts b/src/router/index.ts index 3e49915..35612dc 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,23 +1,23 @@ -import { createRouter, createWebHistory } from 'vue-router' -import HomeView from '../views/HomeView.vue' +import { createRouter, createWebHistory } from "vue-router"; +import HomeView from "../views/HomeView.vue"; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { - path: '/', - name: 'home', + path: "/", + name: "home", component: HomeView, }, { - path: '/about', - name: 'about', + path: "/about", + name: "about", // route level code-splitting // this generates a separate chunk (About.[hash].js) for this route // which is lazy-loaded when the route is visited. - component: () => import('../views/AboutView.vue'), + component: () => import("../views/AboutView.vue"), }, ], -}) +}); -export default router +export default router; diff --git a/src/stores/counter.ts b/src/stores/counter.ts index b6757ba..374b4d0 100644 --- a/src/stores/counter.ts +++ b/src/stores/counter.ts @@ -1,12 +1,12 @@ -import { ref, computed } from 'vue' -import { defineStore } from 'pinia' +import { ref, computed } from "vue"; +import { defineStore } from "pinia"; -export const useCounterStore = defineStore('counter', () => { - const count = ref(0) - const doubleCount = computed(() => count.value * 2) +export const useCounterStore = defineStore("counter", () => { + const count = ref(0); + const doubleCount = computed(() => count.value * 2); function increment() { - count.value++ + count.value++; } - return { count, doubleCount, increment } -}) + return { count, doubleCount, increment }; +}); diff --git a/src/styles/global.css b/src/styles/global.css new file mode 100644 index 0000000..3916d72 --- /dev/null +++ b/src/styles/global.css @@ -0,0 +1,118 @@ +/** + * Global Styles - lukastitch.corneruniverse.com + * + * Base styles and resets that apply site-wide. + * Uses variables from variables.css for theming. + */ + +@import "./variables.css"; + +/* Import Google Fonts */ +@import url("https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700&family=Quicksand:wght@500;600;700&display=swap"); + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + scroll-behavior: smooth; +} + +body { + min-height: 100vh; + font-family: var(--font-body); + font-size: var(--font-size-base); + line-height: 1.6; + color: var(--color-text); + background-color: var(--color-background); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +#app { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* Typography */ +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: var(--font-heading); + color: var(--color-heading); + line-height: 1.3; +} + +h1 { + font-size: var(--font-size-3xl); + font-weight: 700; +} + +h2 { + font-size: var(--font-size-2xl); + font-weight: 600; +} + +h3 { + font-size: var(--font-size-xl); + font-weight: 600; +} + +p { + margin-bottom: var(--space-md); +} + +/* Links */ +a { + color: var(--color-accent); + text-decoration: none; + transition: color var(--transition-fast); +} + +a:hover { + color: var(--color-accent-hover); +} + +/* Images */ +img { + max-width: 100%; + height: auto; + display: block; +} + +/* Buttons */ +button { + font-family: inherit; + cursor: pointer; +} + +/* Section wrapper utility */ +.section { + padding: var(--space-xl) var(--space-lg); +} + +.container { + max-width: var(--max-width); + margin: 0 auto; + padding: 0 var(--space-lg); +} + +/* Focus states for accessibility */ +:focus-visible { + outline: 2px solid var(--color-primary); + outline-offset: 2px; +} + +/* Selection color */ +::selection { + background-color: var(--color-primary-light); + color: var(--color-heading); +} diff --git a/src/styles/variables.css b/src/styles/variables.css new file mode 100644 index 0000000..81f445b --- /dev/null +++ b/src/styles/variables.css @@ -0,0 +1,132 @@ +/** + * Design System Variables - lukastitch.corneruniverse.com + * + * Color Palette: Pastel pink, purple, and grey + * Mood: Warm, cozy, handcrafted feel + * + * AI CODING TOOL INSTRUCTIONS: + * When user mentions colors: + * - "primary color" or "main color" -> --color-primary (pastel pink) + * - "secondary color" or "accent" -> --color-secondary (pastel purple) + * - "background" -> --color-background + * - "make it more pink/purple/grey" -> adjust the relevant color values + */ + +:root { + /* ============================================ + PASTEL COLOR PALETTE + Pink, Purple, and Grey as requested + ============================================ */ + + /* Primary: Soft Pastel Pink */ + --color-primary: #f4a5c0; + --color-primary-light: #fcd5e5; + --color-primary-dark: #e87aa0; + + /* Secondary: Soft Pastel Purple */ + --color-secondary: #c9a5f4; + --color-secondary-light: #e5d5fc; + --color-secondary-dark: #a07ae8; + + /* Neutral: Warm Grey */ + --color-grey: #9e9e9e; + --color-grey-light: #e0e0e0; + --color-grey-dark: #6e6e6e; + + /* Background Colors */ + --color-background: #fefcfd; + --color-background-soft: #fdf5f8; + --color-background-mute: #f8eef2; + + /* Text Colors */ + --color-text: #4a4a4a; + --color-text-light: #6e6e6e; + --color-heading: #3d3d3d; + + /* Border Colors */ + --color-border: #e8dde2; + --color-border-hover: #d4c4cc; + + /* Accent for links and interactive elements */ + --color-accent: #c9a5f4; + --color-accent-hover: #a07ae8; + + /* Status Colors */ + --color-available: #a5d6a7; + --color-sold: #ef9a9a; + + /* ============================================ + TYPOGRAPHY + ============================================ */ + + --font-heading: "Quicksand", "Nunito", sans-serif; + --font-body: + "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + + --font-size-xs: 0.75rem; + --font-size-sm: 0.875rem; + --font-size-base: 1rem; + --font-size-lg: 1.25rem; + --font-size-xl: 1.5rem; + --font-size-2xl: 2rem; + --font-size-3xl: 2.5rem; + + /* ============================================ + SPACING + ============================================ */ + + --space-xs: 0.25rem; + --space-sm: 0.5rem; + --space-md: 1rem; + --space-lg: 2rem; + --space-xl: 4rem; + --space-2xl: 6rem; + + --section-gap: 4rem; + + /* ============================================ + LAYOUT + ============================================ */ + + --max-width: 1200px; + --content-width: 800px; + --gallery-gap: 1.5rem; + --card-radius: 12px; + --border-radius: 8px; + + /* ============================================ + SHADOWS + ============================================ */ + + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12); + + /* ============================================ + TRANSITIONS + ============================================ */ + + --transition-fast: 0.15s ease; + --transition-base: 0.3s ease; + --transition-slow: 0.5s ease; +} + +/* Dark mode adjustments - keeping the pastel feel but adjusted for dark backgrounds */ +@media (prefers-color-scheme: dark) { + :root { + --color-background: #2d2a2c; + --color-background-soft: #363133; + --color-background-mute: #403b3d; + + --color-text: #e8e4e6; + --color-text-light: #b8b4b6; + --color-heading: #f4f0f2; + + --color-border: #4a4547; + --color-border-hover: #5a5557; + + /* Pastels stay similar but slightly more saturated for visibility */ + --color-primary: #f4a5c0; + --color-secondary: #c9a5f4; + } +} diff --git a/src/views/AboutView.vue b/src/views/AboutView.vue index 756ad2a..5864983 100644 --- a/src/views/AboutView.vue +++ b/src/views/AboutView.vue @@ -1,15 +1,53 @@ + + - diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index d5c0217..efa05d6 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -1,9 +1,25 @@ + + -- 2.30.2