Created
December 3, 2025 18:15
-
-
Save jlooper/cf5e18996ce86d00b5393de25fb0b017 to your computer and use it in GitHub Desktop.
Full Cursor Prompt for Family Cardmaker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Prompt for Cursor: Build a “Family Holiday Collage Maker” with Cloudinary (Astro + React + Tailwind) | |
| You are an expert full-stack engineer. | |
| Create a small, clean Astro app called “Family Holiday Collage Maker” that uses Cloudinary to let a family upload photos and generate a single shareable holiday collage image via Cloudinary transformations. | |
| Tech + Setup Requirements | |
| Use Astro as the framework. | |
| Use React components inside Astro for interactive parts. | |
| Use TypeScript where appropriate. | |
| Use Tailwind CSS for styling. | |
| The app should run with npm install and npm run dev without extra steps. | |
| Use environment variables: | |
| PUBLIC_CLOUDINARY_CLOUD_NAME | |
| PUBLIC_CLOUDINARY_UPLOAD_PRESET | |
| Ensure the Cloudinary Upload Widget script is added to the main layout/head: | |
| <script src="https://widget.cloudinary.com/v2.0/global/all.js"></script> | |
| Core User Story | |
| Goal: | |
| A family visits the site, uploads photos, and the app builds a single collage image URL using Cloudinary transformations. The collage: | |
| Uses a festive background image (hosted in Cloudinary). | |
| Overlays up to 3 uploaded images in a simple grid. | |
| Adds a text overlay like: The Looper Family – Holiday 2025. | |
| Shows the final collage preview and a copyable URL. | |
| Pages and Components | |
| 1. Main page: / | |
| Use an Astro page (e.g. src/pages/index.astro) that: | |
| Imports and renders a React component handling the interactive behavior (e.g. FamilyCollageApp.tsx). | |
| Provides a clean layout shell (centered container, basic typography). | |
| 2. React Component: FamilyCollageApp | |
| Create a React component (e.g. src/components/FamilyCollageApp.tsx) that handles: | |
| State: | |
| type Photo = { publicId: string; url: string }; | |
| const [familyName, setFamilyName] = useState("Looper Family"); | |
| const [photos, setPhotos] = useState<Photo[]>([]); | |
| Layout and behavior: | |
| Header | |
| Title: “Family Holiday Collage Maker” | |
| Short subtitle: | |
| “Invite your family to upload photos, then generate a shareable holiday collage powered by Cloudinary.” | |
| Controls | |
| Text input for Family name (default "Looper Family"). | |
| “Add family photos” button that opens the Cloudinary Upload Widget. | |
| Uploads Gallery | |
| When photos.length > 0, show a grid of thumbnails (3 columns on desktop, responsive on mobile). | |
| Each image uses object-cover, rounded corners. | |
| Generated Collage | |
| If no photos: show subtle text “Upload at least one photo to see your collage.” | |
| If there are photos: | |
| Build a collage URL using a helper function (buildCollageUrl). | |
| Display the resulting <img> with the collage. | |
| Show: | |
| A read-only text box with the URL. | |
| A “Copy URL” button that copies to clipboard and briefly shows “Copied!” feedback. | |
| Use Tailwind for styling (e.g. max-w-4xl mx-auto p-6 space-y-8, etc.). | |
| Cloudinary Upload Widget Component | |
| Create a reusable React component, e.g. src/components/UploadWidget.tsx: | |
| Uses window.cloudinary.createUploadWidget. | |
| Config options: | |
| cloudName: import.meta.env.PUBLIC_CLOUDINARY_CLOUD_NAME | |
| uploadPreset: import.meta.env.PUBLIC_CLOUDINARY_UPLOAD_PRESET | |
| sources: ["local", "camera", "url"] | |
| multiple: true | |
| maxFiles: 10 | |
| Accepts an onUpload(info) callback prop that receives info.public_id and info.secure_url. | |
| Type definition: | |
| type UploadWidgetProps = { | |
| onUpload: (info: { public_id: string; secure_url: string }) => void; | |
| }; | |
| Behavior: | |
| Use useRef to store the widget instance and useEffect to initialize it once on the client. | |
| When the widget callback fires with result.event === "success", call onUpload(result.info). | |
| Expose a button that calls widgetRef.current.open(). | |
| Collage URL Logic (Important) | |
| Implement a pure function in a separate file (e.g. src/lib/collage.ts) to build the collage URL from: | |
| cloudName: string | |
| photos: { publicId: string }[] | |
| familyName: string | |
| Assumptions: | |
| Background image public ID: holiday-assets/collage-bg | |
| Final collage dimensions: 1600×900 | |
| Use at most 3 photos for now. | |
| Steps in the URL: | |
| Base: | |
| https://res.cloudinary.com/<cloudName>/image/upload | |
| Transformation segments (in order): | |
| Base size & quality: | |
| w_1600,h_900,c_fill,q_auto,f_auto | |
| For each of up to 3 photos, overlay with positions like: | |
| l_<publicId>,w_450,h_450,c_fill,g_north_west,x_80,y_120 | |
| l_<publicId>,w_450,h_450,c_fill,g_north,y_120 | |
| l_<publicId>,w_450,h_450,c_fill,g_north_east,x_80,y_120 | |
| Note: If publicId contains a folder, e.g. family-uploads/photo1, convert / to : when used as an overlay: l_family-uploads:photo1. | |
| Text overlay for family name: | |
| l_text:Montserrat_60_bold:<URL_ENCODED_TEXT>,co_rgb:ffffff,g_south,y_80 | |
| <URL_ENCODED_TEXT> should be something like Looper%20Family%20–%20Holiday%202025. | |
| Append the background public ID: | |
| holiday-assets/collage-bg | |
| So the final URL shape: | |
| https://res.cloudinary.com/<cloudName>/image/upload/ | |
| w_1600,h_900,c_fill,q_auto,f_auto/ | |
| l_family-uploads:photo1,w_450,h_450,c_fill,g_north_west,x_80,y_120/ | |
| l_family-uploads:photo2,w_450,h_450,c_fill,g_north,y_120/ | |
| l_family-uploads:photo3,w_450,h_450,c_fill,g_north_east,x_80,y_120/ | |
| l_text:Montserrat_60_bold:Looper%20Family%20–%20Holiday%202025,co_rgb:ffffff,g_south,y_80/ | |
| holiday-assets/collage-bg | |
| Implement this assembly in TypeScript with proper URL encoding and defensive checks (e.g., if only 1 or 2 photos are present). | |
| Tailwind + Astro Integration | |
| Configure Tailwind for Astro (standard setup with tailwind.config.cjs, postcss.config.cjs, and integration in astro.config.mjs). | |
| Apply Tailwind classes in both the Astro layout and React components. | |
| Use a main layout (e.g. src/layouts/BaseLayout.astro) that: | |
| Includes the Tailwind-powered <body> shell. | |
| Adds the Cloudinary widget script in the <head>. | |
| Wraps the page content in a centered container. | |
| File Structure (example suggestion) | |
| You may choose a reasonable structure, but here’s a suggested outline: | |
| src/pages/index.astro – main page, imports FamilyCollageApp. | |
| src/components/FamilyCollageApp.tsx – main React app component. | |
| src/components/UploadWidget.tsx – Cloudinary upload widget wrapper. | |
| src/lib/collage.ts – buildCollageUrl helper. | |
| src/layouts/BaseLayout.astro – layout with script + container. | |
| Tailwind + Astro config files. | |
| What to Deliver | |
| All necessary Astro + React + TypeScript + Tailwind setup. | |
| A working flow: | |
| Upload images via Cloudinary Upload Widget. | |
| Show gallery of uploaded photos. | |
| Generate and display a Cloudinary collage URL + image. | |
| Copy-to-clipboard button with simple “Copied!” feedback. | |
| Clean, readable code with comments where helpful. | |
| A minimal README.md explaining: | |
| Required env vars (PUBLIC_CLOUDINARY_CLOUD_NAME, PUBLIC_CLOUDINARY_UPLOAD_PRESET). | |
| How to run the app locally (npm install, npm run dev). | |
| Please now generate the full implementation (all necessary files) so I can run this app immediately in a fresh Astro project. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment