A simple web app for creating and managing cards from URLs. Each card displays the title and description extracted from a URL, with user editing capabilities. You can optionally make your board public for anyone to read it :)
Live demo at https://url-cards.web.app/ (limited user signup for now)
The one big design goal of this project is for anyone to deploy this app on their own firebase project and change the software to fit their own needs. (Like opensource software is supposed to work.) So please feel free to fork and play.
Lots of help from the LLMs, as I'm not a web dev.
- URL-based Cards: Add URLs to create cards with auto-extracted metadata
- Editable Content: Modify card titles and descriptions
- Tagging: Add multi-word tags to cards, manage tag names/colors, and remove tags without deleting cards
- Google Authentication: Secure user login via Google accounts
- Real-time Sync: Cards sync across devices using Firebase
- Responsive Design: Works on desktop and mobile
- Reactions on cards for public view (only with logged in user)
- Add collaborators to a board. (Read only and edit rights)
- Technology: Vanilla JavaScript, HTML5, CSS3
- Hosting: Firebase Hosting
- Authentication: Firebase Auth (Google provider)
- Database: Firebase Firestore
- Functions: Firebase Cloud Functions
- Purpose: URL metadata extraction (handles CORS, fetches page titles/descriptions)
- User enters URL
- Frontend calls Firebase Function to extract metadata
- Card created with title/description (user can edit)
- Card saved to Firestore (user-specific collection)
- Real-time updates across all user's devices
- Node.js 16+
- Firebase CLI (
npm install -g firebase-tools) - Google account
-
Clone and install:
git clone <repo-url> cd cards cd functions && npm install && cd ..
-
Create Firebase project:
- Go to Firebase Console
- Create a new project (or use existing)
- Choose a project location (e.g., africa-south1, us-central1)
- Note your project ID and region (you'll need them in the next steps)
-
Configure region (if not using africa-south1):
Update the region in three files to match your Firebase project region:
public/config.js- ChangeREGIONvaluefunctions/config.js- ChangeREGIONvaluefirebase.json- Changelocationunderfirestore
Example: If your project is in
us-central1, change all three files to use that region. -
Enable required APIs (IMPORTANT):
Note: This requires the gcloud CLI to be installed. If you don't have it, install it from Google Cloud SDK documentation.
Run the automated setup script to enable all required APIs:
./setup-apis.sh your-project-id
This enables:
- Cloud Build API (required for Functions deployment)
- Cloud Functions API (required for Functions)
- Firebase Extensions API (required for deployment)
- Cloud Run API (required for Functions v2)
- Eventarc API (required for event-driven functions)
- Cloud Billing API (required for project billing operations)
Manual alternative: If you prefer, you can enable these APIs manually:
- Go to Google Cloud Console > "APIs & Services" > "Library"
- Search for and enable each API listed above
-
Firebase CLI setup:
firebase login firebase init # Select: Hosting, Functions, Firestore, Authentication # Choose your project # Accept defaults or customize as needed
-
Configure Firestore:
- In Firebase Console, go to Firestore Database
- Create database in the same region as your project
- Default id is fine (leave as is)
- Start in production mode (rules are already in firestore.rules)
-
Set up Functions cleanup policy:
If this is your first time deploying Cloud Functions to this region, set up a cleanup policy to manage old function artifacts:
firebase functions:artifacts:setpolicy --location africa-south1
Replace
africa-south1with your chosen region if different. This prevents storage costs from accumulating old function versions. -
Enable Authentication:
- In Firebase Console, go to Authentication → Sign-in method
- Enable Google provider
- Set project public-facing name and support email
- Click Save
-
Local development:
firebase emulators:start # App available at http://localhost:5000 # Functions at http://localhost:5001 # Firestore UI at http://localhost:4000
-
Deploy:
firebase deployTo enable automatic deployments on push to main:
-
Generate service account:
firebase init hosting:github # Follow prompts to set up GitHub Actions -
Configure service account permissions (IMPORTANT):
The auto-generated service account needs additional permissions to deploy Cloud Functions. Without these, deployment will fail with "Missing permissions" error.
- Go to Google Cloud Console IAM
- Select your project
- Find the GitHub Actions service account (named like
github-action-XXXXX@your-project.iam.gserviceaccount.com) - Edit and add these roles:
- Service Account User
- Cloud Functions Admin
- Firebase Admin
See the Troubleshooting section for detailed instructions.
-
Verify secrets:
- Check that
FIREBASE_SERVICE_ACCOUNT_URL_CARDSis in your GitHub repo secrets - This was created automatically by the Firebase CLI
- Check that
-
Push to deploy:
git push origin main # Triggers automatic deployment
├── public/ # Static frontend files
│ ├── index.html # Main app
│ ├── styles.css # Styling
│ ├── script.js # Frontend logic
│ └── config.js # Region configuration
├── functions/ # Backend functions
│ ├── index.js # URL metadata extraction
│ ├── config.js # Region configuration
│ └── package.json # Function dependencies
├── firestore.rules # Database security rules
├── firebase.json # Firebase configuration
├── .firebaserc # Project settings
├── setup-apis.sh # API setup automation script
└── README.md # This file
- Local development:
firebase emulators:start - Test changes: Hot reload in browser
- Lint functions code:
npm run lint --prefix functions(auto-fix: add-- --fix) - Deploy:
firebase deploy - Auto-deploy: Push to main branch (GitHub Actions)
This project uses a deliberate testing pyramid to keep feedback fast and reliable while still covering real user flows.
Unit tests (Jest) are the default choice for:
- Pure logic in
public/js/rules/*(sorting, validation, tag suggestions/usage, filtering). - Edge cases and input validation that are easier to express as small, deterministic tests.
UI/E2E tests (Playwright) are reserved for:
- Critical user journeys (board creation, renaming, deletion, card flows).
- Integration points where DOM + Firebase + routing interactions matter.
- A small set of smoke tests that ensure the app works end-to-end.
Rule of thumb: prefer unit tests for correctness and breadth, and keep UI/E2E tests focused on user-critical flows to avoid flakiness and slow test runs. See TESTING.md and E2E_TESTING.md for details.
Firebase Free Tier (Spark Plan):
- Hosting: 10GB storage, 360MB/day transfer
- Firestore: 50K reads, 20K writes, 1GB storage/day
- Functions: 125K invocations, 40K GB-seconds/month
- Auth: Unlimited users
Typical monthly cost: $0 for personal use, ~$5-25 for moderate usage
- User authentication required for all operations
- Firestore rules enforce user data isolation
- Functions validate inputs and handle CORS safely
- No API keys exposed to frontend
Problem: Error message says "Cloud Functions deployment requires the Cloud Build API to be enabled"
Solution: Run the setup script to enable all required APIs:
./setup-apis.sh your-project-idManual alternative:
- Go to Google Cloud Console
- Select your Firebase project
- Navigate to "APIs & Services" > "Library"
- Search for and enable:
- Cloud Build API
- Cloud Functions API
- Firebase Extensions API
- Cloud Run API
- Eventarc API
- Cloud Billing API
Problem: Error message says "Functions successfully deployed but could not set up cleanup policy"
Solution: Set up a cleanup policy for your region to manage old function artifacts:
firebase functions:artifacts:setpolicy --location africa-south1Replace africa-south1 with your region. This is typically only needed the first time you deploy functions to a region.
Problem: Error message says "Missing permissions required for functions deploy. You must have permission iam.serviceAccounts.ActAs"
Solution: The service account used by GitHub Actions needs additional permissions.
- Go to Google Cloud Console IAM
- Select your project
- Find the service account (usually named like
github-action-XXXXX@your-project.iam.gserviceaccount.com) - Click the pencil icon to edit
- Add these roles:
- Service Account User (grants iam.serviceAccounts.ActAs)
- Cloud Functions Admin (or Cloud Functions Developer)
- Firebase Admin
- Save changes
Alternatively, use the gcloud CLI:
# Replace SERVICE_ACCOUNT with your actual service account email
gcloud projects add-iam-policy-binding your-project-id \
--member="serviceAccount:SERVICE_ACCOUNT" \
--role="roles/iam.serviceAccountUser"
gcloud projects add-iam-policy-binding your-project-id \
--member="serviceAccount:SERVICE_ACCOUNT" \
--role="roles/cloudfunctions.admin"- Fork repository
- Create feature branch
- Make changes with local emulator testing
- Submit pull request
- Auto-deploy to preview environment