EntryRequirements
Visa and entry rules for a passport: visa-free, travel authorisation, eVisa or visa required, the stay allowed, passport validity, documents and health rules.
Demo
Entering Japan
Tourism · United Kingdom passportNo visa needed
- Stay
- Up to 90 days
- Passport validity
- Valid for the whole stay
Documents
- Return or onward ticket Required
- Visit Japan Web QR codes Recommended Fill in before you land: immigration and customs
Health
- Travel insurance Recommended Japanese hospitals may ask for payment up front
Source: Ministry of Foreign Affairs of Japan. Checked 28 Oct 2026.
Entering United States
Tourism · Portugal passportESTA needed before you travel
Apply at least 72 hours before you fly; it lasts two years. Apply online
- Stay
- Up to 90 days
- Passport validity
- An e-passport, valid for the whole stay
- Visa fee
- $21
Documents
- Approved ESTA Required
- Return ticket Required
- Overstaying once means a visa for every later visit.
Entering India
Business · Canada passportBusiness visa required
- Passport validity
- 6 months beyond arrival, 2 blank pages
- Visa fee
- CA$ 125
Health
- Yellow fever certificate Required Only when arriving from a country with yellow fever
Get it
It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/entry_requirements.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/entry_requirements.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/entry_requirements.qwe
view EntryRequirementsDemo() = <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 12px; align-items: start"> <EntryRequirements id="entry-requirements-demo-jp" destination="Japan" flag="🇯🇵" nationality="United Kingdom" visa="free" stay="Up to 90 days" passport="Valid for the whole stay" documents={[{ label: "Visit Japan Web QR codes", note: "Fill in before you land: immigration and customs" }, { label: "Return or onward ticket", required: true }]} health={[{ label: "Travel insurance", note: "Japanese hospitals may ask for payment up front" }]} source={{ label: "Ministry of Foreign Affairs of Japan", href: "#mofa" }} checked="28 Oct 2026"/> <EntryRequirements id="entry-requirements-demo-us" destination="United States" flag="🇺🇸" nationality="Portugal" visa="eta" visa_name="ESTA" visa_note="Apply at least 72 hours before you fly; it lasts two years." visa_href="#esta" fee="$21" stay="Up to 90 days" passport="An e-passport, valid for the whole stay" documents={[{ label: "Approved ESTA", required: true, href: "#esta" }, { label: "Return ticket", required: true }]} warnings={["Overstaying once means a visa for every later visit."]}/> <EntryRequirements id="entry-requirements-demo-in" destination="India" nationality="Canada" purpose="Business" visa="required" visa_name="Business visa" fee="CA$ 125" passport="6 months beyond arrival, 2 blank pages" health={[{ label: "Yellow fever certificate", required: true, note: "Only when arriving from a country with yellow fever" }]}/> </div>Source
ui/entry_requirements.qwe
// EntryRequirements: what a traveller needs to enter a country, for their passport:// whether a visa is needed (none, an online travel authorisation, an eVisa, a visa on// arrival, or a visa to get before going), how long one may stay, how long the// passport must still be valid, the documents to carry and the health rules, each// marked as required or recommended, with warnings, where it was checked and when.//// <EntryRequirements destination="Japan" nationality="United Kingdom" visa="free" stay="Up to 90 days"// passport="Valid for the whole stay"// documents={[{ label: "Return or onward ticket", required: true },// { label: "Visit Japan Web QR codes", note: "For immigration and customs" }]}// source={{ label: "Ministry of Foreign Affairs of Japan", href: "https://www.mofa.go.jp/" }}// checked="28 Oct 2026"/>//// destination: the country entered; nationality: whose passport ("United Kingdom").// flag: an emoji or short text before the destination. purpose: what for ("Tourism",// the default; "Business", "Study"). visa: "free" (no visa), "eta" (an online travel// authorisation: ESTA, ETA, ETIAS), "evisa", "on_arrival" or "required"; visa_name:// what it is called ("ESTA", "Schengen visa"); visa_note: a line under it ("Apply at// least 72 hours before you fly"); visa_href: where to apply; fee: what it costs, as// text ("$21", already written). stay: how long one may stay ("Up to 90 days in any// 180"). passport: how long it must still be valid ("6 months beyond your stay").// documents, health: lists of { label, required?: true (else recommended), note?,// href? }. warnings: lines in a warning box ("Rules change at short notice"). source:// { label, href } of the official page; checked: when the rules were last checked. id,// class.import ui/theme.qwe use ui_clslet entry_requirements_css = css`.ui-entryreq { --ui-entryreq-k: var(--ui-success); --ui-entryreq-ks: var(--ui-success-soft); display: grid; gap: 16px; min-width: 0; max-width: 560px; padding: 18px 20px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); color: var(--ui-fg); font: 14px/1.45 var(--ui-font); }.ui-entryreq-info { --ui-entryreq-k: var(--ui-info); --ui-entryreq-ks: var(--ui-info-soft); }.ui-entryreq-warning { --ui-entryreq-k: var(--ui-warning); --ui-entryreq-ks: var(--ui-warning-soft); }.ui-entryreq-danger { --ui-entryreq-k: var(--ui-danger); --ui-entryreq-ks: var(--ui-danger-soft); }.ui-entryreq-head { display: grid; gap: 2px; }.ui-entryreq .ui-entryreq-title { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 18px; font-weight: 700; }.ui-entryreq-flag { font-size: 22px; line-height: 1; }.ui-entryreq-for { color: var(--ui-muted); font-size: 13px; }.ui-entryreq-verdict { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 4px 12px; align-items: start; padding: 14px 16px; border-left: 4px solid var(--ui-entryreq-k); border-radius: var(--ui-radius-sm); background: var(--ui-entryreq-ks); }.ui-entryreq-icon { grid-row: span 2; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; background: var(--ui-entryreq-k); color: var(--ui-surface); }.ui-entryreq-icon svg { width: 18px; height: 18px; }.ui-entryreq-verdict b { font-size: 16px; font-weight: 700; }.ui-entryreq-verdict p { margin: 0; font-size: 13px; }.ui-entryreq-verdict a { color: var(--ui-fg); font-weight: 600; }.ui-entryreq-verdict a:focus-visible { outline: none; border-radius: 2px; box-shadow: var(--ui-focus); }.ui-entryreq-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin: 0; }.ui-entryreq-facts div { display: grid; align-content: start; gap: 2px; padding: 10px 12px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); }.ui-entryreq-facts dt { color: var(--ui-muted); font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; }.ui-entryreq-facts dd { margin: 0; font-weight: 600; }.ui-entryreq-part { display: grid; gap: 6px; }.ui-entryreq .ui-entryreq-sub { margin: 0; color: var(--ui-muted); font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; }.ui-entryreq-list { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }.ui-entryreq-item { display: grid; grid-template-columns: 20px minmax(0, 1fr) auto; gap: 2px 10px; align-items: start; padding: 8px 0; border-bottom: 1px solid var(--ui-border); }.ui-entryreq-item:last-child { border-bottom: 0; }.ui-entryreq-item svg { width: 18px; height: 18px; margin-top: 1px; color: var(--ui-muted); }.ui-entryreq-must svg { color: var(--ui-danger); }.ui-entryreq-item a { color: var(--ui-accent); }.ui-entryreq-item small { grid-column: 2; color: var(--ui-muted); font-size: 12px; }.ui-entryreq-tag { grid-column: 3; grid-row: 1; padding: 1px 8px; border-radius: 999px; background: var(--ui-subtle); color: var(--ui-muted); font-size: 11px; font-weight: 600; white-space: nowrap; }.ui-entryreq-must .ui-entryreq-tag { background: var(--ui-danger-soft); color: var(--ui-fg); }.ui-entryreq-warn { margin: 0; padding: 10px 14px 10px 32px; border-radius: var(--ui-radius-sm); background: var(--ui-warning-soft); font-size: 13px; }.ui-entryreq-warn li + li { margin-top: 4px; }.ui-entryreq-foot { margin: 0; color: var(--ui-muted); font-size: 12px; }.ui-entryreq-foot a { color: inherit; }`let entry_requirements_icons = { ok: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>, doc: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3h7l5 5v13H7z"/><path d="M14 3v5h5M10 13h6M10 17h6"/></svg>, stop: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><path d="M12 7v6M12 17v.5"/></svg>, globe: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 3 2.5 15 0 18M12 3c-2.5 3-2.5 15 0 18"/></svg>,}// What a visa status says: { title, tone, icon }.fn entry_requirements_verdict(visa: str, name: str?) -> map = match visa { "free" => { title: "No visa needed", tone: "success", icon: "ok" } "eta" => { title: name != null ? `${name} needed before you travel` : "Travel authorisation needed", tone: "info", icon: "globe" } "evisa" => { title: name != null ? `${name} needed` : "eVisa needed", tone: "info", icon: "globe" } "on_arrival" => { title: name != null ? `${name} on arrival` : "Visa on arrival", tone: "warning", icon: "doc" } _ => { title: name != null ? `${name} required` : "Visa required", tone: "danger", icon: "stop" } }// A list of documents or health rules, the required ones first.fn entry_requirements_list(items: list) = <ul.ui-entryreq-list> {for it in (items | filter((x) => x.required == true)) + (items | filter((x) => x.required != true)) => { let must = it.required == true <li class={ui_cls(["ui-entryreq-item", must && "ui-entryreq-must"])} data-required={must ? "true" : "false"}> {must ? entry_requirements_icons.stop : entry_requirements_icons.ok} <span>{it.href != null ? <a href={it.href}>{it.label}</a> : it.label}</span> {" "}<span.ui-entryreq-tag>{must ? "Required" : "Recommended"}</span> {it.note != null && [" ", <small>{it.note}</small>]} </li> }} </ul>view EntryRequirements(destination: str, nationality: str, visa: str = "free", flag: str? = null, purpose: str = "Tourism", visa_name: str? = null, visa_note: str? = null, visa_href: str? = null, fee: str? = null, stay: str? = null, passport: str? = null, documents: list = [], health: list = [], warnings: list = [], source: map? = null, checked: str? = null, id: str? = null, class: str? = null) { style global entry_requirements_css let v = entry_requirements_verdict(visa, visa_name) let base = id ?? `entry-requirements-${re.replace(lower(destination), "[^a-z0-9]+", "-")}` <section class={ui_cls(["ui-entryreq", `ui-entryreq-${v.tone}`, class])} id={id} aria-labelledby={`${base}-title`} data-visa={visa}> <div.ui-entryreq-head> <h3.ui-entryreq-title id={`${base}-title`}> {flag != null && <span.ui-entryreq-flag aria-hidden="true">{flag}</span>} Entering {destination} </h3> <span.ui-entryreq-for>{purpose} · {nationality} passport</span> </div> <div.ui-entryreq-verdict role="note"> <span.ui-entryreq-icon>{entry_requirements_icons[v.icon]}</span> <b>{v.title}</b> {(visa_note != null || visa_href != null) && <p>{visa_note}{visa_note != null && visa_href != null && " "}{visa_href != null && <a href={visa_href}>Apply online</a>}</p>} </div> {(stay != null || passport != null || fee != null) && <dl.ui-entryreq-facts> {stay != null && <div data-fact="stay"><dt>Stay</dt><dd>{stay}</dd></div>} {passport != null && <div data-fact="passport"><dt>Passport validity</dt><dd>{passport}</dd></div>} {fee != null && <div data-fact="fee"><dt>{visa == "free" ? "Fee" : "Visa fee"}</dt><dd>{fee}</dd></div>} </dl>} {len(documents) > 0 && <div.ui-entryreq-part> <h4.ui-entryreq-sub id={`${base}-docs`}>Documents</h4> {entry_requirements_list(documents)} </div>} {len(health) > 0 && <div.ui-entryreq-part> <h4.ui-entryreq-sub id={`${base}-health`}>Health</h4> {entry_requirements_list(health)} </div>} {len(warnings) > 0 && <ul.ui-entryreq-warn aria-label="Warnings">{for w in warnings => <li>{w}</li>}</ul>} {(source != null || checked != null) && <p.ui-entryreq-foot> {source != null && ["Source: ", source.href != null ? <a href={source.href} rel="noopener">{source.label}</a> : source.label]} {source != null && checked != null && ". "} {checked != null && `Checked ${checked}.`} </p>} </section>}Related
FlightStatusA flight's status today: on time, delayed, boarding, in the air or cancelled, with the new times beside the old, gate changes and the baggage belt.9 views · 12 downloadsPackingListWhat to pack, in groups: a checkbox for each thing with how many, a bar of how much is packed, things to add or take off, packed ones hidden and all unpacked again.7 views · 7 downloadsTripItineraryA trip day by day: flights, trains, stays, activities and meals in order of time, with booking references, status, today's day marked and the trip counted up.9 views · 10 downloadsCheckListItems shown done or not with ticks and crosses (or empty circles), read-only, with an optional count.5 views · 8 downloadsNotificationListNotifications grouped by day (Today, Yesterday, then dates), read or unread, with links, mark-read and dismiss buttons and Mark all as read.7 views · 6 downloadsOrderHistoryPast orders with number, date, status and total, each opening on <details> to show its items and links.6 views · 5 downloads
Thread
Thread 0 posts
On EntryRequirements: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.