Repository navigation
feat: cut over educates.dev to the redesigned site - #55
Merged
Merged
Conversation
Define the site's domain language: Content and its kinds, Featured Content, the Content hub and Topics, Features and use cases, and the demo vocabulary (Demo Platform, Session, Demo, Hands-on event and the Sponsor, Builder, Presenter and Attendee roles).
ADR 0001 chooses plain Astro 7 for educates.dev. ADR 0002 keeps the Docusaurus URL form (no trailing slash, page.html files) and the blog feeds' item identity. The Docusaurus docs plugin publishes everything under docs/, so its exclude list now adds adr/** to the plugin's defaults, and ADRs never become pages while Docusaurus still builds the site.
The Hub will become a section of educates.dev, so the site cannot have two hubs. The glossary defines Educates Hub, and the page that lists all Content becomes the Learn page.
Click-to-enlarge on diagrams is a small inline script, so React islands are limited to the Asciinema player and the Learn page's filters.
Build educates.dev with Astro 7 and npm instead of Docusaurus and Yarn, on Node 26 pinned through Volta with engines.node at >=24. Pages build as <path>.html with no trailing slash, as ADR 0002 records. Prettier with its Astro plugin formats code files only. The kept assets move from static/ to public/, with a root favicon.ico added; CNAME, the unused Featured Content images and customer logos, and the old screenshots are dropped. The Content directories stay in place, unbuilt.
npm run build now runs the site check over dist/ after astro build. It reads the output as GitHub Pages serves it: /page from page.html, /dir/ from dir/index.html, case-sensitive. The committed must-resolve list holds the 67 pages of the old sitemap, the 404 page, both feeds and their stylesheets, the crawler and host files, the kept assets, the 24 feed images and the redirect sources; an entry the build does not serve is a warning for now. The command exits 1 when a rule reports an error.
Every page needs one canonical URL and a matching og:url, both the absolute https://educates.dev URL the page is served at, without .html, /index, a trailing slash, a query or a fragment. A page built as <path>/index.html fails too, since GitHub Pages serves it only with a trailing slash. Redirect pages are skipped.
@astrojs/sitemap always writes an index and numbered parts, so a build step renames sitemap-0.xml to sitemap.xml and deletes the index. The site check fails when the sitemap is missing, split, or lists a URL that is not a page in the site's URL form: a redirect page, the 404 page, a file or a URL the build does not serve.
The tokens are CSS custom properties named by role in one file, src/styles/tokens.css, with direction D's light and dark values plus the Session dashboard's product colors, under :root and the theme attribute only. src/styles/tokens.ts reads the same file, so the light values are available to JavaScript. The theme follows prefers-color-scheme until the visitor uses the toggle, which sets data-theme on the root element and remembers the choice in localStorage; an inline head script applies a stored choice before first paint. Blocked storage leaves the page on the system theme, and the toggle is hidden without JavaScript.
BaseLayout writes the title with the site name, the description, the canonical URL and og:url built without .html or /index, the Open Graph basics, the site-wide JSON-LD and the page's section label as data-section on <body>. It loads Onest and JetBrains Mono at 400, 600 and 700 from the site itself, the tokens and the theme script, and wraps the page in a skip link, a minimal header with the theme toggle and <main>. The placeholder homepage carries the hero copy; the 404 page links home and to the Learn page.
The workflows build with npm on Node 26 and upload dist/. The Dockerfile has a dev target running the Astro dev server and a serve target serving the build with nginx, for the host architecture. The README lists the npm commands and the site check.
The serve target builds the site, runs the site check and serves dist/ through nginx with GitHub Pages' URL behavior: /page serves page.html, a URL ending in a slash serves only its directory's index.html, a directory URL without the slash redirects to add it, misses get the site's 404.html, and the feeds and their XSL stylesheets are typed as XML. The dev target runs the Astro dev server over a mounted checkout, keeping the image's Linux dependencies in an anonymous volume. npm run docker-build builds a target as educates-dev:<target> with Node at the Volta pin, for the host architecture unless TARGET_PLATFORMS lists platforms.
The checks workflow, on pull requests to develop and main, replaces the test build: astro check, the Prettier check, the build, the site check, the unit tests, lychee offline over dist/ (a broken internal link fails it) and Lighthouse on a mobile profile, the median of three runs, on the homepage, a use case page, a Feature deep page, a blog post and /learn. Accessibility or SEO below 90 fails; performance below 90 warns. Each check runs even when an earlier one fails. Node comes from the Volta pin. lychee.toml holds the link settings both link checks share, and npm run link-check runs the internal check locally.
Build main with withastro/action@v6, which runs npm run build, the site check included, at the Node version of the Volta pin, and publish it with actions/deploy-pages@v5. Only the deploy job gets the Pages and ID token permissions, and deployments do not overlap.
Every Monday, and by hand, build the site and run lychee online over its http and https links, leaving educates.dev itself to the internal check. Dead links open or update the one "Dead external links" issue; a clean run closes it. The run never fails on a dead link, only when lychee itself fails.
Dependabot groups the minor and patch updates of each ecosystem into one pull request and gives each major update a pull request of its own, so a new Astro major is never missed. Commit messages follow Conventional Commits: build(deps) and build(deps-dev) for npm, ci(deps) for actions.
The README lists npm run link-check and npm run docker-build, explains the serve and dev targets and TARGET_PLATFORMS, and summarizes the checks, deploy and external links workflows and Dependabot.
Each link uses GitHub's latest-release download URL, so the Downloads page stays current with every platform release.
The project pages and the homepage strip share one call to action that follows the site's Sponsors setting.
…3.8.0 runs The 3.8.0 CLI creates its Kind cluster with Kind v0.32.0's default node image, kindest/node:v1.36.1, so the package repositories move from v1.31 to v1.36 and the sample output shows kubectl v1.36.5 with its bundled Kustomize v5.8.1.
The install commands fetch the latest release, which is 3.8.0, and its `educates version` prints only the version number.
The 3.8.0 CLI sets no node image, so Kind v0.32.0 uses its default, kindest/node:v1.36.1. The config path now prints on one line, as the CLI's Println does, and the status lines keep the leading space Kind prints them with.
3.8.0 installs Kyverno v1.15.1, which ships no report cleanup CronJobs, and Contour v1.30.2, whose certgen Job carries that version in its name. Its ClusterPolicy columns no longer include VALIDATE ACTION, and the baseline set no longer has restrict-apparmor-profiles: sixteen policies in all.
…ates The hugo template now writes a security.token block under the Session's namespaces and indents its lists as the template does, so the example matches the file a reader opens. The examiner lines move to 28-29.
List, page by page, the versions and sample outputs in the Getting Started Guides that follow an Educates release, where each comes from at the release tag, and how to refresh them for the next release and for 4.0.
Add an Example kind to a use case's proof links, for something built on Educates outside the project's own publications, and list Graham Dumpleton's labs and a Next.js front end on the lookup service as examples to study on the Demo Platform page.
List his public labs, self-paced courses hosted on Educates, as an example in the Customer and partner enablement proof.
A What you bring point can name an example built on Educates, by its link text and URL, shown after its docs link. The Demo Platform's front end and sign-in points link the Apache 2.0 OAuth front end on the lookup service as a place to start.
Customer and partner enablement and Team training both leave sign-in through your identity provider to a front end of yours; link the Apache 2.0 OAuth front end as a place to start.
A workshop, Educates Feature Tour, whose pages each set up a screenshot or recording of the Features pages: clickable actions, examiner checks, the editor, console and slides, the Session's Docker daemon, registry and Git server, its namespace and quota, the portal REST API, the lookup service, workshop definitions, the Terraform modules' usage and a course written with the AI authoring skills. Two more definitions run pages of the same content on a virtual cluster and with no Kubernetes access. Example Academy is a small front end of the kind a training team builds: it lists workshops from the lookup service and starts Sessions, embeds the training portal, receives its analytics events, and runs an examiner check from outside a Session.
Every Feature on the overview gets a screenshot, and each deep page its loop and a screenshot for each thing, with alt text describing what each shows: 51 screenshots and 6 muted loops, taken from Educates 4.0 as built from develop, next to their entries.
A manifest names every screenshot and loop on the Features pages: the Feature entry and slot it fills, where it is taken, the steps that set it up, its window and its alt text. The tool drives Chrome through puppeteer-core against a training portal of its own, records loops through the DevTools screencast into muted H.264 MP4 with a poster, renders terminals on this machine with asciinema-player, and wires each capture into its entry. Its setup and teardown deploy and remove the capture workshop, the lookup service configuration and Example Academy. Tests check that the manifest fills every visual slot with one shot of its kind and that every shot is wired into its entry.
The header gains a Project menu with Get started, Get help, Community and Downloads, sharing its links with the footer's Project column. The phone sheet lists it with the other menus. Four menus no longer fit beside the header's links below 1024px, so the phone menu now replaces the header menus from 1023px down (PHONE_MENU), while the page layouts keep switching at 960px (PHONE_LAYOUT).
Copy and source corrections from the review of every drafted page: - Homepage: a shorter hero caption, new frame captions, and "Ways to get help" on the strip's help link. - Use cases: the SpringOne 2020 figure, sourced to VMware's "Open Sourcing Educates"; the academies fact cites ADOPTERS.md; Viam Education and the NWS Playground as named examples; portals pick a workshop's pathway; Team training's sign-in goes through the lookup service, as the front-end example's title now says. - Features: Built-in services offers injected SSH key pairs for remote machines; examiner checks and local authoring headlines no longer say "learner" or overclaim; alt texts speak of the Session or a person; "Limits" as the deep page heading; the AI page pins skill version 4.1 and names only the authoring skill's releases. - History: SpringOne 2020, the Learning Center fork and the November 2023 acquisition, each with its source, and all five adopters named. - Get help starts with the docs and names the #educates channel; Learn's intro, the PyCon AU description and the Working locally topic; the privacy page no longer lists language among what is counted.
Chrome Stable stops running XSLT on 2026-11-17, and Firefox and WebKit plan to follow, so the styled feed pages would break soon after launch. The RSS and Atom feeds keep their URLs and entries but no longer link a stylesheet. The XSL files, the CSS routes that served them, and their shared stylesheet module go. The must-resolve list no longer requires the four stylesheet URLs, which only the feeds ever linked; the list header and ADR 0002 record the exception. nginx serves only .xml files as XML.
Air-gapped install, shown once Educates 4.0 is released, gets its visual: a terminal listing the digest-pinned image list the 4.0.0-alpha.10 release publishes, kept as a fixture. The capture tooling changes to take it: - 4.0-only Features have visual slots, so `captures check` and the manifest test expect their shots before the 4.0 setting turns on. - The capture portal and Example Academy are looked up when a shot first needs them, so terminal shots run without the capture setup. - A terminal wider than the default 100 columns gets a smaller font in proportion, so its window still fits the capture window.
A Feature's docs link that points at GitHub opens a repository README, so "Read the docs" promised something it did not deliver. One rule, docsLinkLabels in src/lib/features.ts, now labels every Feature docs link by where it points: a GitHub link reads "Read the README" on the deep page, "README" on the Features overview and "What the README says" under a limit; any other keeps "Read the docs", "Docs" and "What the docs say".
ArrowLink laid out its label and icon with inline-flex, so a label that wrapped, such as a use case page's long Example title, filled the line and pushed the arrow or external mark to the column's far edge. The link now flows as text. A word joiner before the icon, in a span that does not wrap, keeps the icon on the label's last line, 6px after the last word, and never alone on a line. The rendered label is trimmed, so a label written on its own line leaves no space before the icon. Unwrapped links render as before; only the focus ring of a link in running text is now the text's height rather than the line's. Screen-reader context after a label, such as the title after "What the docs say", moves into a screenReaderContext prop, so no caller can leave a line break, and so a space, between the label and the icon.
The KCD Spain 2023 talk has a Spanish title that screen readers read as English. An outside entry can now name its language with an optional `lang`, a BCP 47 tag such as `es`, and the talk sets `lang: es`. Wherever the entry's card shows, on the Learn page, in Featured Content and in a post's Related Content, the title is wrapped in a span with that `lang`. The " (external site)" note for screen readers stays outside it, in English. Entries without a `lang` render as before. A `lang` that is not a well-formed BCP 47 tag, such as `es_ES`, fails the build; `isLanguageTag()` in src/lib/outside-content.ts checks it.
Related Content took the first three entries sharing any Topic in the Learn page's order, newest first, so a post showed recent entries that had little to do with it: the cloud provider install posts listed unrelated recent posts, and "Your first workshop" listed AI posts. relatedEntries() now puts the entries sharing the most Topics with the post first, and among those, the closest in date before or after it. An undated entry, such as a guide, counts as farther than any dated one, and remaining ties keep the Learn page's order. The parts of the post's own series are left out, since its series box lists them; a Content entry now carries its post's series for that. A post with fewer than three candidates shows what there is, and one with none shows no section.
Two visuals on the Lookup service deep page did not show what their text describes. "Your own front end on one portal" showed a terminal calling the portal's REST API, and "A tenant for each customer" showed the tenant configuration in the editor. The first now shows Example Academy's catalog for the person signed in, built on one training portal's REST API with its robot account alone: each workshop with the Sessions it has free, and Resume on the one they already have a Session for. The second shows two customers' own sites side by side, Acme Training and Globex Academy, each logging in to the lookup service with a client granted only its own tenant, and listing the workshops of its own portal. To take them, the capture setup creates a portal, a tenant and a client for each customer, and passes the portal's robot account and the customers' clients to Example Academy. Example Academy serves the catalog at /portal and the customers' sites at /customers/<id>. The capture tool takes two pages of Example Academy side by side, and gives a site shot that starts a Session a browser of its own. The terminal and editor shots these replace are gone; their setup steps move to the next shots of the same Session.
The privacy page now says Google Analytics ran until the cutover, 2026-10-09, and its last-updated date matches. The site check no longer warns about the cutover date placeholder.
feat: redesign educates.dev on Astro
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Cutover: publishes the redesigned educates.dev (#54) on the live domain.
Also carries "Updating dependencies" (71352bf), which was on
developbut never reachedmain; the redesign replaces the files it touched.Evidence
npm run url-check -- https://educates.dev(Docusaurus):98 of 111 URLs resolve.After (expected once deployed):
111 of 111 URLs resolve, as the same build already does in theserveimage. Checks on feat: redesign educates.dev on Astro #54: site check0 errors, 0 warningswith the live sitemap comparison, 374 unit tests, lychee0 errors, Lighthouse assertions passing on 5 URLs.Merge Danger
Door: two-way
Rollback: revert this merge commit on
main(git revert -m 1 <merge>) in a pull request, merged as an administrator, since the old workflows never report the required "Checks". The deploy then publishes the Docusaurus site again. Once the new site is live, Google Analytics stops receiving data and GoatCounter starts counting; a rollback would load Google Analytics again.Blast Radius: site-wide
Every page of educates.dev changes. Old URLs keep working as pages or redirect pages. The Pages custom domain and HTTPS setting are unchanged.