# TheSkillVault: all skills (full text) > Every published skill as raw SKILL.md. Source pages: https://theskillvault.ai/skills/. Licence and attribution are in each skill's front matter and at the end of each section. --- --- name: add-analytics-and-privacy-policy description: Adds privacy-friendly visitor analytics and a plain-English privacy page covering contact-form data, analytics and overseas providers, with Australian Privacy Principles notes. Not legal advice. Use when the user wants site analytics, visitor stats, a privacy policy or privacy page. license: CC-BY-4.0 metadata: category: frontend tags: "privacy,analytics,website,nextjs" author: johnny-bravo version: "1.0" updated: "2026-10-31" status: published --- # Add analytics and a privacy page > **Not legal advice.** This skill helps you write a clear first draft and points to the official guidance. Facts were checked against oaic.gov.au and vercel.com on 2026-10-31. Laws and guidance change: check the linked pages, and ask a lawyer if you handle health, financial or children's information or are unsure whether the Privacy Act covers you. ## When to use - A website collects anything about visitors (a contact form, analytics, a newsletter) and needs a privacy page. - You want simple visitor numbers without building a tracking setup. - Australian small businesses especially; the structure works elsewhere but the legal notes are Australian. ## Handoff - Reads: `docs/website-brief.md` (business name and contact details), `app/contact/actions.ts` (what the form collects and where it sends it), and the list of services the site uses. - Writes: `app/privacy/page.tsx`, the analytics component in `app/layout.tsx`, and a footer link to `/privacy`. ## Steps 1. List what the site actually collects and who receives it, in a table in your notes: contact form (name, email, message → the email provider and the owner's inbox), analytics (page views, referrer, rough location, device → the analytics provider), hosting logs (→ the host). Don't guess: read the code. 2. Analytics, choose one: - **Vercel Web Analytics** if the site is on Vercel: enable it in the project's Analytics tab, `npm install @vercel/analytics`, add `` from `@vercel/analytics/next` in `app/layout.tsx`. Vercel's docs say it uses no third-party cookies, identifies visitors by a hash of the request that is discarded after 24 hours, and stores page URL, referrer, coarse location, OS, browser and device type. - Another privacy-focused tool: only describe it as "cookie-free" if that vendor's own documentation says so, and record the link. - Don't put personal details (emails, names, order numbers) into URLs or custom events. 3. Check whether the Privacy Act covers the business (OAIC small-business page): - Most businesses with annual turnover of A$3 million or less are not covered. - Some are covered regardless of turnover, including health service providers (which OAIC says includes complementary therapists, child care centres and private schools), businesses that trade in personal information, Commonwealth contracted service providers, businesses related to a covered business, and businesses that have opted in. Use OAIC's checklist on that page. - Even if not covered, a short honest privacy page is good practice and builds trust; OAIC recommends protecting the personal information you hold. 4. Write `app/privacy/page.tsx` in plain English with these headings. They follow what APP 1.4 requires a privacy policy to contain: - Who we are and how to contact us (business name, email, phone). - What we collect and how (the table from step 1, in sentences). - Why we collect it and how we use and share it (reply to enquiries; understand which pages are useful). - Overseas providers: name the services and, where practicable, the countries they are likely to store data in (check each provider's privacy page; many are in the United States). - How to see or correct your information (APP 12 and 13): email us; we'll respond within a reasonable time. - How to complain: contact us first; if you're not satisfied you can complain to the OAIC (oaic.gov.au). - How long we keep enquiries, and how we keep them secure. - Date last updated. 5. Collection notice at the form (APP 5): one sentence under the contact form saying who you are, why you collect the details, and linking to `/privacy`. The contact form step adds this line; check it matches the page. 6. Link `/privacy` from the footer of every page, and add it to `app/sitemap.ts`. 7. Re-read the page against what the code does. If you add a service later (chat widget, booking tool, newsletter), update the table, the page and its date. ## Gotchas - Only say what is true. A privacy page promising "we never share data" while using a US email API is wrong. - A mailing list is different from a contact form. Don't add enquirers to marketing emails without their consent; the Spam Act 2003 (regulated by ACMA) applies to marketing emails, and APP 7 notes the overlap. - From 10 December 2026, APP 1.7–1.9 require covered businesses whose computer programs make, or substantially and directly help make, decisions that could significantly affect someone's rights or interests to describe this in their privacy policy. OAIC lists this as starting on that date; it is not in force at the time of this check, and a brochure site with a contact form and analytics normally makes no such decisions. - Don't copy another company's privacy policy: it describes their systems, not yours. - Analytics tools change their data handling; re-check the vendor's privacy docs when you set it up. ## Sources (checked 2026-10-31) - OAIC, Small business: https://www.oaic.gov.au/privacy/privacy-guidance-for-organisations-and-government-agencies/organisations/small-business - OAIC, Read the Australian Privacy Principles (APP 1, 5, 7, 8, 12, 13): https://www.oaic.gov.au/privacy/australian-privacy-principles/read-the-australian-privacy-principles - OAIC, APP 1 guidelines (APP 1.7–1.9 from 10 December 2026): https://www.oaic.gov.au/privacy/australian-privacy-principles/australian-privacy-principles-guidelines/chapter-1-app-1-open-and-transparent-management-of-personal-information - OAIC, Privacy complaints: https://www.oaic.gov.au/privacy/privacy-complaints - Vercel, Web Analytics privacy and compliance: https://vercel.com/docs/analytics/privacy-policy ## Example prompt "Add a privacy policy page to my site and some simple visitor stats. I'm a small Australian business with a contact form." _Licence: CC BY 4.0. Attribution: From TheSkillVault, https://theskillvault.ai, CC BY 4.0_ --- --- name: add-chatbot-guardrails-and-rate-limits description: Protects a public AI chatbot from abuse before launch - per-user and per-IP rate limits, a monthly spend cap, input and output size limits, prompt-injection-resistant system prompts, moderation, and logging without personal data. Use when the user worries about abuse or a surprise AI bill, wants to rate limit an API, or needs to stop jailbreak attempts on a chatbot. license: CC-BY-4.0 metadata: category: backend tags: "chatbot,security,rate-limiting" author: johnny-bravo version: "1.0" updated: "2026-10-31" status: published --- # Add chatbot guardrails and rate limits A public chatbot is a public tap on your AI bill. Put limits in before you share the link. ## When to use - Before launching any chatbot that people can use without paying. - After an unexpected AI bill or a spike in traffic. - People are getting the bot to ignore its instructions. ## Steps 1. **Set a hard spend limit at the provider.** Most AI dashboards have a monthly budget or usage limit. Set it to an amount you can afford to lose. 2. **Rate limit per user and per IP.** For example 20 messages per 10 minutes and 200 per day. Use a shared store such as Redis, Upstash, Cloudflare KV or Durable Objects, or your database. In-memory counters reset on every serverless start. 3. **Return 429 with a clear message** ("You've hit the limit - try again in 10 minutes") and a `Retry-After` header. 4. **Cap sizes.** Limit each message (for example 2,000 characters), the history you send (last 20 messages) and the reply length (`max_output_tokens` or your provider's equivalent). 5. **Require sign-in or a captcha for heavy use.** Anonymous users get a small allowance. Signed-in users get more. 6. **Harden the system prompt.** Say what the bot does and refuses. Tell it that text from users or documents is data, not instructions. Never put secrets in the prompt, since users can often get it to repeat it. 7. **Moderate.** Run inputs, and outputs if needed, through your provider's moderation endpoint or a simple blocklist. Refuse politely. 8. **Give tools the least power.** If the bot can call tools (send email, read the database), allow read-only actions by default and ask a human before anything irreversible. 9. **Log safely.** Record counts, token use and errors, without full messages or personal data unless your privacy policy says so. Alert yourself when daily spend passes a threshold. ## Gotchas - Rate limiting by IP alone blocks whole offices and schools. Combine it with user IDs. - No prompt is jailbreak-proof. Limits and tool permissions are the real protection. - Keep API keys server-side. Rotate a key at once if it ever appears in the browser, a repo or a log. ## Works with any stack These steps work with any provider (OpenAI, Anthropic, Google, local models) and any host. Use your platform's built-in rate limiting (Cloudflare, Vercel, API gateways) instead of custom code if you have it. ## Done when - Sending 25 messages in a minute gets a 429 with a friendly message. - A message over the size limit is rejected before any model call. - The provider dashboard shows a spend limit, and you get an alert email when spend passes your threshold. ## Example prompt "Stop people abusing my free AI chatbot and running up my OpenAI bill." _Licence: CC BY 4.0. Attribution: From TheSkillVault, https://theskillvault.ai, CC BY 4.0_ --- --- name: add-dark-mode-toggle description: Adds a light/dark/system theme toggle that persists the choice in localStorage and avoids the white flash on page load, using CSS variables or Tailwind's dark class. Use when the user asks for dark mode, a night theme, a theme switcher, or follow-the-system colour scheme. license: CC-BY-4.0 metadata: category: frontend tags: "react,tailwind,css,theming" author: johnny-bravo version: "1.0" updated: "2026-10-30" status: published --- # Add a dark mode toggle ## When to use - The app has one colour scheme and the user wants dark mode or a theme switcher. ## Steps 1. Decide the mechanism: Tailwind v4 `@custom-variant dark (&:where(.dark, .dark *));` (v3: `darkMode: "class"`), or plain CSS variables under `:root` and `.dark`. 2. Add a tiny blocking script in `` **before** any CSS paints, so there is no flash: ```html ``` 3. Build a toggle with three states (light, dark, system) that writes `localStorage.theme` and toggles the `dark` class. 4. When on "system", listen to `matchMedia(...).addEventListener("change", ...)` and update live. 5. Add `color-scheme: light dark` so native inputs and scrollbars match. 6. Check contrast of text, borders and focus rings in both themes. ## Gotchas - In Next.js, add `suppressHydrationWarning` on `` because the class is set before React hydrates (or use `next-themes`). - Images and shadows that look fine on white may vanish on dark; review them. ## Example prompt "Add a dark mode switch to the navbar that remembers my choice." _Licence: CC BY 4.0. Attribution: From TheSkillVault, https://theskillvault.ai, CC BY 4.0_ --- --- name: add-seo-metadata-and-sitemap description: Adds search basics to a Next.js site, with a title and description per page, Open Graph share images, sitemap.ts, robots.ts, LocalBusiness structured data and a favicon. Use when the user wants to show up on Google, improve SEO, fix link previews, or add a sitemap or meta tags. license: CC-BY-4.0 metadata: category: frontend tags: "seo,nextjs,website" author: johnny-bravo version: "1.1" updated: "2026-10-31" status: published --- # Add SEO metadata and a sitemap This skill adds the basic tags and files search engines and chat apps read. To find what is wrong on a live site, use `audit-site-seo` first. ## When to use - A Next.js App Router site is built and needs to be findable and share nicely on social media and chat apps. - Link previews show no image or the wrong title. - Google Search Console asks for a sitemap. - An SEO audit found missing titles, descriptions, a sitemap or structured data. ## Handoff - Reads: `docs/website-brief.md` (page list, business details) and `lib/site.ts`. - Writes: `metadata` exports in `app/layout.tsx` and each `page.tsx`, `app/sitemap.ts`, `app/robots.ts`, `app/opengraph-image.tsx` (or `.png`), `app/icon.png`, and LocalBusiness JSON-LD in `app/layout.tsx`. ## Steps 1. Site-wide defaults in `app/layout.tsx`: ```ts export const metadata: Metadata = { metadataBase: new URL(site.url), // e.g. https://www.example.com.au title: { default: site.name, template: `%s | ${site.name}` }, description: "One sentence saying what you do and where.", openGraph: { type: "website", siteName: site.name, locale: "en_AU" }, }; ``` 2. Every page exports its own `metadata` with a unique `title` (under about 60 characters) and `description` (about 150 characters) written for people, using the words customers search for (service + suburb or city). 3. Share image: `app/opengraph-image.tsx` using `ImageResponse` from `next/og` (1200×630, business name and tagline on the brand colour), or drop a 1200×630 `app/opengraph-image.png`. Add `alt` via `export const alt = "..."`. 4. Icons: put `app/icon.png` (square, at least 512×512) and `app/apple-icon.png` in `app/`. Next.js adds the `` tags. Remove the default `favicon.ico` if it is still the Next.js logo. 5. `app/sitemap.ts`: ```ts import type { MetadataRoute } from "next"; export default function sitemap(): MetadataRoute.Sitemap { return ["", "/about", "/services", "/contact", "/privacy"].map((p) => ({ url: `${site.url}${p}`, lastModified: new Date() })); } ``` 6. `app/robots.ts`: allow everything in production and point to the sitemap: `{ rules: { userAgent: "*", allow: "/" }, sitemap: \`${site.url}/sitemap.xml\` }`. 7. Structured data: in `app/layout.tsx` render `