Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# launchcal
# launchcal: SpaceX launch calendar

[![ci](https://github.com/launchcal/launchcal.github.io/actions/workflows/ci.yml/badge.svg)](https://github.com/launchcal/launchcal.github.io/actions/workflows/ci.yml)
[![publish](https://github.com/launchcal/launchcal.github.io/actions/workflows/publish.yml/badge.svg)](https://github.com/launchcal/launchcal.github.io/actions/workflows/publish.yml)
[![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-roma321m-FFDD00?logo=buymeacoffee&logoColor=black)](https://buymeacoffee.com/roma321m)

Free, self-updating calendars of SpaceX launches. Subscribe once and every new launch, reschedule
and scrub shows up in your calendar, in your own time zone.
A free, self-updating SpaceX launch calendar for Google Calendar, Apple Calendar (iPhone, iPad, Mac)
and Outlook. Subscribe once and every Falcon 9, Falcon Heavy, Starship and crewed Dragon launch,
reschedule and scrub shows up in your calendar, in your own time zone. Separate calendars cover
Starship flights only, crewed launches only, and everything except Starlink.

**Website: https://launchcal.github.io**

Expand Down
5 changes: 5 additions & 0 deletions site/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ document.addEventListener('click', async (event) => {
}, 1500);
});

// Schedule: launches with a known time are rendered in UTC; show them in the visitor's time zone instead.
for (const time of document.querySelectorAll('time[data-local]')) {
time.textContent = new Date(time.dateTime).toLocaleString(undefined, { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', timeZoneName: 'short' });
}

// Next launch: the first launch in next.json that has not ended on the visitor's clock.
const panel = document.getElementById('next');
fetch('next.json')
Expand Down
Binary file added site/img/crs-20-launch-1277.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/img/crs-20-launch-640.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/img/crs-20-launch-960.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed site/img/crs-20-launch.jpg
Binary file not shown.
Binary file added site/img/og.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/img/screens/google-agenda.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/img/screens/google-event.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/img/screens/google-week.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions site/privacy.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Privacy policy · launchcal</title>
<meta name="description" content="launchcal collects no personal data: no accounts, no cookies, no analytics.">
<link rel="canonical" href="https://launchcal.github.io/privacy.html">
<meta name="theme-color" content="#000000">
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="style.css">
Expand Down
4 changes: 4 additions & 0 deletions site/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
User-agent: *
Allow: /

Sitemap: https://launchcal.github.io/sitemap.xml
6 changes: 6 additions & 0 deletions site/sitemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://launchcal.github.io/</loc></url>
<url><loc>https://launchcal.github.io/terms.html</loc></url>
<url><loc>https://launchcal.github.io/privacy.html</loc></url>
</urlset>
56 changes: 56 additions & 0 deletions site/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,62 @@ footer {
gap: 10px;
}

.upcoming {
padding: 72px 0;
border-top: 1px solid var(--line);
}

.section-intro {
max-width: 46em;
margin: 0 0 24px;
color: var(--muted);
}

/* Launch schedule: date, mission and site stacked on phones, in columns on wide screens. */
.schedule {
margin: 0;
padding: 0;
list-style: none;
}

.schedule li {
display: grid;
gap: 2px;
padding: 14px 0;
border-bottom: 1px solid var(--line);
}

.schedule time {
color: var(--muted);
font-variant-numeric: tabular-nums;
}

.launch-name {
font-weight: 700;
}

.launch-site {
color: var(--muted);
font-size: 0.95rem;
}

@media (min-width: 900px) {
.schedule li {
grid-template-columns: 15em 1fr 1fr;
gap: 24px;
align-items: baseline;
}
}

.schedule-more summary {
margin-top: 24px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.14em;
font-size: 0.85rem;
cursor: pointer;
}

.faq dl {
margin: 0;
max-width: 46em;
Expand Down
1 change: 1 addition & 0 deletions site/terms.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Terms of use · launchcal</title>
<meta name="description" content="Terms of use, disclaimers and credits for launchcal, an unofficial SpaceX launch calendar.">
<link rel="canonical" href="https://launchcal.github.io/terms.html">
<meta name="theme-color" content="#000000">
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="style.css">
Expand Down
6 changes: 5 additions & 1 deletion src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ for (const { calendar, events } of feeds) {
console.log(`site/cal/${calendar.id}.ics: ${events.length} events`);
}

await writeFile('site/index.html', renderPage(await readFile('src/page.html', 'utf8'), CALENDARS));
const flown = new Set(['Success', 'Failure', 'Partial Failure']);
const upcoming = launches
.filter((launch) => !flown.has(launch.status) && Date.parse(launch.net) + 3_600_000 > Date.now())
.sort((a, b) => Date.parse(a.net) - Date.parse(b.net) || a.name.localeCompare(b.name));
const next = nextLaunches(all, new Date(), NEXT_COUNT);
await writeFile('site/index.html', renderPage(await readFile('src/page.html', 'utf8'), CALENDARS, next[0], upcoming));
await writeFile('site/next.json', JSON.stringify(next) + '\n');
console.log(`site/index.html, site/next.json: ${next.length} upcoming`);
2 changes: 1 addition & 1 deletion src/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface CalendarEvent {
confirmed: boolean;
}

const TIMED_PRECISIONS = new Set(['SEC', 'MIN', 'HR']);
export const TIMED_PRECISIONS = new Set(['SEC', 'MIN', 'HR']);

const CONFIRMED_STATUSES = new Set(['Go', 'In Flight', 'Success', 'Failure', 'Partial Failure']);

Expand Down
52 changes: 35 additions & 17 deletions src/page.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,31 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>launchcal: SpaceX launches in your calendar</title>
<meta name="description" content="Free, unofficial, self-updating calendars of SpaceX launches for Google Calendar, Apple Calendar and Outlook.">
<title>SpaceX Launch Calendar for Google, Apple and Outlook | launchcal</title>
<meta name="description" content="Free SpaceX launch calendar that updates itself: every Falcon 9, Falcon Heavy, Starship and crewed launch in Google Calendar, Apple Calendar or Outlook, in your time zone.">
<link rel="canonical" href="https://launchcal.github.io/">
<meta name="theme-color" content="#000000">
<meta property="og:title" content="launchcal: SpaceX launches in your calendar">
<meta property="og:description" content="Free, unofficial, self-updating calendars of SpaceX launches.">
<meta property="og:image" content="https://launchcal.github.io/img/crs-20-launch.jpg">
<meta property="og:type" content="website">
<meta property="og:site_name" content="launchcal">
<meta property="og:title" content="SpaceX Launch Calendar for Google, Apple and Outlook">
<meta property="og:description" content="Every SpaceX launch in your calendar, updated automatically: Falcon 9, Falcon Heavy, Starship and crewed flights.">
<meta property="og:url" content="https://launchcal.github.io/">
<meta property="og:image" content="https://launchcal.github.io/img/og.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="A Falcon 9 rocket lifting off at night from its launch pad.">
<meta name="twitter:card" content="summary_large_image">
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="preload" href="fonts/D-DIN-Bold.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="fonts/D-DIN.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" as="image" href="img/crs-20-launch-960.webp" imagesrcset="img/crs-20-launch-640.webp 640w, img/crs-20-launch-960.webp 960w, img/crs-20-launch-1277.webp 1277w" imagesizes="(min-width: 900px) 46vw, 100vw">
<!-- structured-data -->
<link rel="stylesheet" href="style.css">
<script src="app.js" defer></script>
</head>
<body>
<header class="hero">
<img class="hero-photo" src="img/crs-20-launch.jpg" width="1277" height="1920" alt="A Falcon 9 rocket lifting off at night, its flame reflected in the water in front of the launch pad.">
<img class="hero-photo" src="img/crs-20-launch-960.webp" srcset="img/crs-20-launch-640.webp 640w, img/crs-20-launch-960.webp 960w, img/crs-20-launch-1277.webp 1277w" sizes="(min-width: 900px) 46vw, 100vw" width="1277" height="1920" fetchpriority="high" alt="A Falcon 9 rocket lifting off at night, its flame reflected in the water in front of the launch pad.">
<div class="topbar">
<p class="wordmark">launchcal</p>
<nav class="project-links" aria-label="Project">
Expand All @@ -27,16 +37,10 @@
</div>
<div class="hero-text">
<h1>SpaceX launches<br>in your calendar</h1>
<p class="lede">Free, unofficial calendars that keep themselves up to date. Scrubs and new launch times show up within hours, in your own time zone.</p>
<p class="lede">A free, unofficial SpaceX launch calendar that keeps itself up to date: Falcon 9, Falcon Heavy, Starship and crewed flights. Scrubs and new launch times show up within hours, in your own time zone.</p>
<a class="btn btn-primary" href="#calendars">Choose a calendar</a>

<section class="next" id="next" hidden>
<h2 id="next-heading">Next launch</h2>
<p class="next-title" id="next-title"></p>
<p class="next-when" id="next-when"></p>
<p class="next-clock" id="next-clock"></p>
<p class="next-meta" id="next-meta"></p>
</section>
<!-- next -->
</div>
</header>

Expand All @@ -45,15 +49,15 @@ <h2 id="next-heading">Next launch</h2>
<h2>What it looks like</h2>
<div class="shots">
<figure class="shot-main">
<img src="img/screens/google-event.jpg" width="800" height="560" loading="lazy" alt="Google Calendar showing the Falcon 9 Crew-13 event: launch time, pad, status, booster, spacecraft, crew, orbit and webcast link.">
<img src="img/screens/google-event.webp" width="800" height="560" loading="lazy" alt="Google Calendar showing the Falcon 9 Crew-13 event: launch time, pad, status, booster, spacecraft, crew, orbit and webcast link.">
<figcaption>Every launch carries the details: time, pad, booster and landing, crew, webcast.</figcaption>
</figure>
<figure>
<img src="img/screens/google-agenda.jpg" width="780" height="800" loading="lazy" alt="Google Calendar schedule on a phone listing upcoming Falcon 9 and Falcon Heavy launches with their local times.">
<img src="img/screens/google-agenda.webp" width="780" height="800" loading="lazy" alt="Google Calendar schedule on a phone listing upcoming Falcon 9 and Falcon Heavy launches with their local times.">
<figcaption>Upcoming launches on your phone, in your time zone.</figcaption>
</figure>
<figure>
<img src="img/screens/google-week.jpg" width="800" height="560" loading="lazy" alt="Google Calendar week view with Starship and Falcon 9 launches as one-hour events.">
<img src="img/screens/google-week.webp" width="800" height="560" loading="lazy" alt="Google Calendar week view with Starship and Falcon 9 launches as one-hour events.">
<figcaption>Launches sit in your week next to everything else, marked as free time.</figcaption>
</figure>
</div>
Expand All @@ -67,9 +71,21 @@ <h2>Pick a calendar</h2>
</div>
</section>

<section class="upcoming" id="schedule">
<h2>Upcoming SpaceX launches</h2>
<p class="section-intro">The SpaceX launch schedule as it stands now. Dates are only as precise as they are known; launches without a firm day are not in the calendars yet.</p>
<!-- schedule -->
</section>

<section class="faq">
<h2>Good to know</h2>
<dl>
<dt>How do I add SpaceX launches to Google Calendar?</dt>
<dd>Pick a calendar above and press "Add to Google Calendar", then confirm. It appears in your calendar list on the web, Android and iPhone, and updates on its own.</dd>
<dt>How do I add them to my iPhone, Apple Calendar or Outlook?</dt>
<dd>Press "Apple / Outlook" to subscribe directly, or copy the ICS link. On iPhone: Settings → Calendar → Accounts → Add Account → Other → Add Subscribed Calendar. In Outlook: Add calendar → Subscribe from web.</dd>
<dt>Can I follow only Starship or crewed launches?</dt>
<dd>Yes. Besides every SpaceX launch, there are calendars for Starship flights, for crewed Dragon missions, and for everything except routine Starlink launches.</dd>
<dt>How fresh is it?</dt>
<dd>Launch data is checked every two hours. Google calendars update right away; Apple and Outlook refresh on their own schedule, usually a few times a day.</dd>
<dt>Why are some launches all-day or marked (TBD)?</dt>
Expand All @@ -78,6 +94,8 @@ <h2>Good to know</h2>
<dd>No. Events show as free time and have no reminders of their own; add one to any launch you want to watch.</dd>
<dt>How do I unsubscribe?</dt>
<dd>Remove the calendar in your calendar app, like any other subscribed calendar.</dd>
<dt>Where does the launch schedule come from?</dt>
<dd>From Launch Library 2 by The Space Devs, a community-run database of launches. launchcal is not affiliated with SpaceX.</dd>
</dl>
</section>
</main>
Expand Down
Loading
Loading