Travel

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.

#travel#documents#status#list#no-js

Demo

Preview

Entering Japan

Tourism · United Kingdom passport
No 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 passport
ESTA 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

  • Overstaying once means a visa for every later visit.

Entering India

Business · Canada passport
Business 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.qwe
main.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>}
Thread
98Useful? Like it, or add to its thread below.

Thread 0 posts

On EntryRequirements: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.