From happy-trip-site
Generate mobile-first travel itinerary websites from natural-language trip plans, spreadsheet-pasted itineraries, chat notes, or rough travel schedules. Use when the user wants a phone-readable trip site, shareable URL, backup package, itinerary webpage, or one-stop workflow from travel text to published travel guide. The skill must ask follow-up questions until the trip brief is complete before generating files or deploying.
How this skill is triggered — by the user, by Claude, or both
Slash command
/happy-trip-site:happy-trip-siteThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Turn a natural-language itinerary into a static, mobile-first travel website with a shareable URL first and a static backup package preserved. Generate locally first, validate, confirm the page style, run the mobile usability gate, then publish through the fastest available URL path. Vercel through `npx vercel` is the v1 default, not the product identity.
assets/static-template/assets/css/travel.cssassets/static-template/assets/js/travel-helpers.jsassets/static-template/assets/js/travel-map.jsassets/static-template/assets/js/travel-ui-components.jsassets/static-template/assets/js/travel.jsassets/static-template/index.htmlassets/static-template/vercel.jsonreferences/architecture.mdreferences/design-principles.mdreferences/design-reference.cssreferences/extraction-rules.mdreferences/itinerary-schema.mdreferences/vercel-deploy.mdscripts/verify-preview.mjsTurn a natural-language itinerary into a static, mobile-first travel website with a shareable URL first and a static backup package preserved. Generate locally first, validate, confirm the page style, run the mobile usability gate, then publish through the fastest available URL path. Vercel through npx vercel is the v1 default, not the product identity.
SKILL_DIRin commands below = the folder thisSKILL.mdlives in. Installed, that is~/.claude/skills/happy-trip-site(or~/.codex/skills/happy-trip-site); working from the source repo it isskill/happy-trip-site. The verifier and template paths are relative to it, not to the user's project CWD — substitute the real path when you run a command. The static template ships notravel-data.js; you write that file yourself (step 9), you do not copy it.
The generated site is meant to be used on a phone while traveling. Treat one-tap external navigation as core functionality:
Google Maps, 官网, 食べログ, 小红书, or 预约.links array so it becomes a tappable button.Do not create files, generate the site, link a Vercel project, or deploy until all of these are true:
references/itinerary-schema.md.普通朋友/同行人分享.done state), visible link buttons, selected or candidate hero image, 3 to 5 plain-language traits, and a recommendation reason tied to the trip. The itinerary cards MUST reproduce the deployed site's core travel-day interactions, not a stripped-down sample: a per-item check-off control (.it-check) with at least one card shown in the completed done state, the per-day progress indicator (X/Y complete with its bar), and a numbered route stop list (route-pin-index + full stop name). These are the most-used on-trip controls, so the user must see them when choosing a style..tmp/<trip-slug>/ui-previews/option-a.html, option-b.html, and option-c.html, showing real trip content rather than abstract swatches.confirmed_option_id, requested a mix that you have recorded as a full UI option, or explicitly delegated the recommended default.Delegation does not hide UI decisions: user wording such as "你选", "用推荐默认", "自己决定", "先生成看看", or "测试 skill 能力" can delegate the recommended UI option, but the final input and generated output must still contain the complete UI Brief. Image selection is automatic by default; do not ask the user to approve individual image slots unless they explicitly request media control.
The UI Brief completion state must explicitly record sharing_context_confirmed, style_selected, and style_revision_recorded.
If information is incomplete, ask one to three targeted questions. Explain why the missing information matters. Never use placeholder images, sample images, or the old Kansai visual style as a fallback.
references/itinerary-schema.md.references/extraction-rules.md.references/design-principles.md (and consult references/design-reference.css for the CSS vocabulary).city/date, ask before designing.aesthetic block (texture feTurbulence data-URI, motif data-URI, glyph mark, display/body font names + Google-Fonts link) derived from the Four Axes; omit any layer for which nothing clean applies. Mark one option as recommended..tmp/<slug>/ui-previews/option-a.html) with the Write tool. Each preview MUST render the cultural aesthetic layer (texture + motif + glyph + display font), not just the palette, using real trip content (title, first day, first two items). It MUST also reproduce the deployed site's core itinerary interactions so they can be judged at style-selection time: per-item check-off boxes (.it-check) with one item shown in the done (struck-through) state, the per-day X/Y complete progress bar, and a numbered route stop list. A preview that only shows palette + cards but omits the check-off / progress / numbered stops does not pass this gate. Strongly prefer rendering the preview through the real engine: in the preview HTML, <link> the template's assets/css/travel.css, define a one-day window.HAPPY_TRIP_DATA slice (the chosen option's aesthetic block + day 1 + its first items, each with the rich note/subtitle/sections/image/mapStopLabels/tag density from itinerary-schema.md), and load the template's JS so the preview is a true thumbnail of production. Only if you hand-write the preview HTML instead, you MUST read the deployed render functions in assets/static-template/assets/js/travel.js (renderItem, renderDayFeature, renderRoutePins) and travel-ui-components.js (renderItemSections) and reproduce their exact class names AND the full richness — the structured it-sections 2-column grid, the inline it-map-seq numbered badge, per-type tag-<tag> colored pills, and the it-media photo with its texture overlay — so the preview can never read as simpler or flatter than what actually deploys. When rendering through the real engine: the deployed runtime derives the done state purely from localStorage, so a fresh preview shows zero done cards and 0/N. To satisfy the "one item shown in done" gate, seed localStorage in the preview before the engine script runs — set key happyTrip.<tripSlug>.done to a JSON object marking the first item done, e.g. {"d1-morning-0":true} (item id format is d<dayN>-<period>-<idx>); and note that the progress denominator is the full day's item count (e.g. a 5-item day reads 1/5), not the "two cards" of the legacy hand-written path. Render the fullest, most complete day in the preview (not the thinnest), so the "no highlight reel" density is judged on real content. Then verify every preview before showing it: run node SKILL_DIR/scripts/verify-preview.mjs <preview>.html (zero-dependency; uses node built-ins only) on each file and FIX every ✗ FAIL before continuing. It catches the silent failures a visual glance and a grep both miss — %2523 double-encoded textures that render nothing, mapStopLabels that don't byte-match a route stop (badge silently drops), a missing aesthetic block (neutral-theme fallback), and thin days. Show the user the local paths only after the verifier passes.<city> skyline". Never use an image URL whose content you have not verified matches the place — a curl 200 only proves the URL is alive, not that the photo shows the right landmark. Try these content-verifiable sources in order, per image (see the full recipe in references/itinerary-schema.md):
WebFetch https://en.wikipedia.org/api/rest_v1/page/summary/<Article_Title> and use originalimage.source (else thumbnail.source). The article title is the subject, so the lead image is usually on-topic. Strongest option for natural landmarks, temples, old towns. WARNING — REST lead images for companies, brands, and modern towers are frequently a corporate logo, coat-of-arms, SVG diagram, or map, not a photograph (e.g. Petronas_Towers, Kuala_Lumpur_Tower return logos). Before using a REST image, confirm the URL is a .jpg/.png photograph of the place and not a logo/SVG/diagram; if it looks like a logo or the source filename contains logo/seal/.svg, discard it and fall back to Commons file search (step 2).WebFetch https://commons.wikimedia.org/w/api.php?action=query&format=json&list=search&srnamespace=6&srlimit=5&srsearch=<place>; pick a result whose filename contains the landmark keywords, then use https://commons.wikimedia.org/wiki/Special:FilePath/<Exact_File_Name>?width=1600.WebFetch the source page first and confirm its title/description matches the subject before using it; otherwise discard it.
Record a reason on every image documenting the verification (e.g. "Wikipedia article Kek Lok Si lead image" / "Commons filename contains Kek_Lok_Si_Temple"). Then curl-verify each final URL returns 200. Never guess hashed Wikimedia /thumb/<hash>/ paths.aesthetic block, automatic images, output folder, assumptions, and deployment target. Get explicit user confirmation (and explicit production confirmation before a production deploy).assets/static-template/ into $HOME/Desktop/<trip-slug>-travel-site/ with Write, then write assets/js/travel-data.js assigning window.HAPPY_TRIP_DATA from the Trip Brief, the UI Brief (including the chosen option's aesthetic block), and the Media Brief. Use the brief→runtime field mapping in references/itinerary-schema.md ("Generated Runtime Data") — the runtime reads meta.tripTitle, ui.confirmed_option, meta.hero, and days[N-1].hero, not the raw brief field names, so a wrong mapping silently renders the neutral fallback theme.index.html, assets/js/travel-data.js, assets/js/travel.js, assets/js/travel-helpers.js, and vercel.json exist; grep window.HAPPY_TRIP_DATA in travel-data.js; grep the viewport meta in index.html; grep a link button and a Google Maps entry. Also run node SKILL_DIR/scripts/verify-preview.mjs <output-folder>/assets/js/travel-data.js — the verifier accepts any file containing the window.HAPPY_TRIP_DATA assignment, so point it at travel-data.js to re-check the silent-failure invariants (texture encoding, map-stop label integrity, aesthetic block, day density) on the real generated data, and fix any FAIL before publishing. Record mobile_usability_passed in mobile-usability-result.json.zip -r <slug>-backup.zip <output-folder>, run npx vercel --prod --yes in the folder, capture the URL, smoke-test with curl -sf <url> | grep -q viewport. Return the shareable URL first when ready_to_share; otherwise return package_ready or blocked with the backup path and the exact missing step.For runtime maintenance, debugging, or contract changes, read references/architecture.md before editing template files. Ordinary trip generation does not require loading it.
<trip-slug>-travel-site.Media Brief entries use direct image URLs:
{
"siteHero": {
"url": "https://example.com/image.jpg",
"source_name": "Wikimedia Commons",
"source_url": "https://example.com/source-page",
"alt": "Gardens by the Bay Supertree Grove, Singapore",
"query": "Gardens by the Bay Supertree Grove"
},
"dayHeroes": {
"day-1": {
"url": "https://example.com/day-1.jpg",
"source_name": "Official tourism site",
"source_url": "https://example.com/day-1-source",
"alt": "Gardens by the Bay at dusk",
"query": "Singapore Gardens by the Bay dusk"
}
}
}
ready_to_share: local validation passed, UI style was confirmed or delegated, mobile usability passed, publishing succeeded, URL smoke test passed, and the generated folder plus static backup package exist.package_ready: the static site and backup package exist, but a shareable URL was not published or could not be trusted.blocked: required trip facts are missing, UI style is not confirmed, generation failed, validation failed, mobile usability failed before packaging, files cannot be written, or the environment cannot preserve artifacts.mobile_usability_passed in mobile-usability-result.json must be true before a URL can be treated as shareable.
Do not report ready_to_share just because a human could finish a missing step. Report the maximum honest state supported by the current agent environment.
Only say local generation is complete when local validation passes. Only say deployment is complete when production smoke testing also passes. If Vercel fails, report the failed stage and keep the local generated folder.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub owenleezy/happy-trip-site --plugin happy-trip-site