Repository navigation
video-gallery, map and link-effects - #21
Merged
Merged
Conversation
…be until a click A grid of YouTube, Vimeo or self-hosted videos behind the site's own posters, with optional category chips (<button aria-pressed>, "All" first, a polite count). mode="lightbox" plays in one <dialog> per gallery (Escape, backdrop, focus return, scroll lock, autoplay on open); mode="inline" turns the tile into the player and puts the previous one back. Without JavaScript each tile links to the video's own page and every video is listed. The URL parser is a verbatim copy of video-player's block; the new scripts/check-media-and-links.mjs fails if the copies drift, greps every built page for anything fetched from Google, YouTube or Vimeo at load, pins the no-JS render and the ARIA contract, and runs each check against broken copies of its input (15 mutations) so a check that cannot fail is caught. Search: "video gallery" 590/mo, difficulty 20 (SE Ranking US, 2026-09-26). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One card per location: the address in an <address>, a tel: link, an optional hours line, and "Open in Google Maps" / "Directions" links to Google's keyless Maps URLs, named with the place. `embed` adds a facade (the location's photo, or a neutral panel with Font Awesome Free's pin) that is a <button> named "Show the map of <name>" and described by "Loads a map from Google."; pressed, it becomes a lazy google.com/maps?…&output=embed iframe titled "Map of <name>". Nothing is fetched from Google before that. Without JavaScript: the cards and links, no facade. Not the Maps JavaScript API: it needs a key and billing, loads Google's script on every visit, and adds Google's origins to every client CSP and consent policy. That is the notFor case. LocalBusiness JSON-LD stays with business-hours. The file is LocationMap.astro so an import cannot shadow JavaScript's Map. check-media-and-links pins the URL builders, the no-JS render, the ARIA, the FA Free pin and the fallback contrast, with 10 more mutations. Search: "google map embed code for website" 170/mo, difficulty 35 (SE Ranking US, 2026-09-26); "map embed without api key" has no data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
underline-slide, underline-grow, brackets, highlight, box, strike-to-underline, arrow, circle, and three of ours: peek (the link's data-peek words slide in, width kept so nothing shifts), swash (an inline SVG underline drawn by stroke-dashoffset) and tag (a pill that fills, the text turning with it). Classes on any <a> (lk-<effect>), or this same file as a <Link effect> wrapper; <LinkEffects /> emits the stylesheet once per page through an Astro.locals guard. Every effect is one `:is(:hover, :focus-visible)` rule, keeps the focus ring, and has its transitions only under prefers-reduced-motion: no-preference. Text colour is never transitioned: highlight pins --lk-ink, and tag paints text and fill as two layers of one background moved by one background-position, so each pixel of text is a passing pair mid-sweep. check-media-and-links computes the fallback pairs (14.75, 11.68, 7.27 and 7.27 to 1), pins the rules above and the demo page, with 13 more mutations. Search: "underline animation css" 590/mo, difficulty 20 (SE Ranking US, 2026-09-26); "link hover effects" is 90/30. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Round 3, Chip C: three elements, one commit each.
npm run checkandnpm run buildare green. The website'snpm run check:typesagainst this branch (ELEMENTS_SOURCE, a scratch clone of SuperheroTech main) gives 0 errors and 0 warnings; its 12 hints are the ones main already has. The website'snpm run buildandcheck:elementsalso pass with this branch.All three are checked by one new script,
scripts/check-media-and-links.mjs, added last to thecheckchain. It greps every built page for anything fetched from Google, YouTube or Vimeo at load: an iframe, script, link, img, source, video poster, embed or object.<noscript>is not exempt. It also runs each check against broken copies of its input. There are 38 of these mutations, and one the check misses fails the run.video-gallery
What it does. A grid of YouTube, Vimeo or self-hosted videos, each behind the site's own poster and play button, with captions under the tiles. Optional category chips filter the grid.
mode="lightbox"plays in one<dialog>per gallery.mode="inline"turns the tile into the player, and pressing another tile puts the first one back, so one plays at a time. A tile without a poster is a neutral panel, never YouTube's thumbnail. The URL parser is a verbatim copy of video-player's block, not an import.Accessibility.
<button type="button">named "Play: <title>", witharia-haspopup="dialog"in lightbox mode.<button aria-pressed>, "All" first and pressed. A polite live region says how many are shown ("Open films: 3 videos.").Without JavaScript. Every tile is a link to the video's own page, named "Watch “<title>” on YouTube", or to the file itself. Every video is listed and the chips are hidden. The one-line mount script straight after the gallery swaps links for buttons before the first paint.
What the check pins.
dnt=1embed.<iframe>or<video>at load.hiddenand aria-pressed order, and the live region.no-preference.Search.
video gallery: 590/mo, difficulty 20 (SE Ranking US, 2026-09-26).alsoRanks:Demo. Big Buck Bunny and Sintel from Blender's YouTube channel (both CC BY 3.0, credited under each tile). Big Buck Bunny's copy on Vimeo, for a Vimeo tile. The self-hosted drift loop with its captions. These are the films video-player's demo uses and names.
map
Why not the Google Maps JavaScript API. It needs an API key and a billing account, loads Google's script on every visit whether or not anyone looks at the map, and adds Google's map origins to every client's CSP and consent policy. So this element is location cards with keyless links and a map that loads only on request. A styled or branded interactive map is the
notForcase, quoted as its own piece of work.What it does.
<address>, an optionaltel:link and an optional hours line.maps/search/?api=1&query=) and "Directions" (maps/dir/?api=1&destination=).embed, a facade: the location's photo, or a neutral CSS street panel with Font Awesome Free's location-dot, read at build as the icon element does.<iframe src="https://www.google.com/maps?q=…&output=embed" loading="lazy" title="Map of <name>">.layout="split"sets the map beside the details. Several locations make a list of cards.LocationMap.astro, notMap.astro, so an import cannot shadow JavaScript'sMap. The id is stillmap.Accessibility.
<button>named "Show the map of ". It is described by the visible notice "Loads a map from Google.", which stays after loading.Without JavaScript. The cards and links only. The facade is rendered
hiddenand the mount script straight after the map shows it, so a button that cannot work never shows.What the check pins.
tel:digits.<address>, both links to Google's keyless URLs with nokey=, and names that start with the visible words and include the place.type="button"named "Show the map of " and described by a notice that says Google.www.google.com/maps?q=…(&z=)&output=embed, and the iframe title is "Map of ".alt="". The pin is Font Awesome Free's location-dot, path for path.loading="lazy"and the title, and is on the page once. The mount script sits straight after each map.Search.
google map embed code for website: 170/mo, difficulty 35 (SE Ranking US, 2026-09-26). The round-3 note's "map embed without api key" and "google map embed without api key" have no data.alsoRanks:"store locator" (2,400/65) is a different product and stays in
aka.Demo. Public Chicago landmarks (Millennium Park, Navy Pier, Garfield Park Conservatory), so the map has something real to show. The one phone number is a 555-01xx number, a range reserved for fiction.
link-effects
What it does. Eleven CSS-only hover-and-focus styles for links. Eight are familiar:
underline-slide,underline-grow,brackets,highlight,box,strike-to-underline,arrowandcircle. Three are ours:data-peekwords slide up in its place, "Pricing" to "$49/mo". Both share one grid cell, so the width is kept and nothing shifts.stroke-dashoffset1 to 0.<LinkEffects />emits the stylesheet once per page, with the modal'sAstro.localsguard. The same file imported asLinkis the<Link effect href>wrapper, which adds the label span for peek and the SVG for swash. The classes are prefixedlk-(class="lk-underline-slide"), because barebox,tagorhighlightwould collide with host CSS. The effect names stay unprefixed.Accessibility.
:is(:hover, :focus-visible)rule, and the browser's focus ring is never removed.prefers-reduced-motion: no-preference, so otherwise the end state appears at once.highlightpins--lk-inkat rest and when active, so a host's owna:hovercolour cannot land on the marker.tagpaints its text colour and its fill as two layers of one background, one of thembackground-clip: text, moved by a singlebackground-position. So mid-sweep each pixel of text is ink on the page or white on the fill, never in between. This was checked on a frame captured halfway through a slowed transition.content: "→" / ""), and the swash SVG is aria-hidden.Without JavaScript. Identical; there is none.
What the check pins.
:hover.no-preference, and no transition ofcoloror-webkit-text-fill-color.outline: none, and an empty alternative on decorative content.background-position.pathLength="1", and peek's markup is complete. No script appears in the demo.Search.
underline animation css: 590/mo, difficulty 20 (SE Ranking US, 2026-09-26). The round-3 note's "link hover effects" is 90/30. Five of the eleven effects are underlines, so the page leads with them.alsoRanks:"css hover effects" (480/31) covers buttons and images and stays unclaimed.
Verified in a browser
I ran headless Chromium against this gallery's build, and against the website built from this branch on both the element pages and the
/elements/hub. On the hub the runtimes become external scripts.dnt=1embed; a file with a captions track; inline one-at-a-time; one column at 375px; the no-JS links; no fade under reduced motion.Follow-ups (not in this PR)
frame-srcallows onlychallenges.cloudflare.com. The video-player and video-gallery iframes (youtube-nocookie.com, player.vimeo.com) and the map embed (www.google.com) are report-only violations today. They would be blocked once that header is promoted to enforced, unless those origins are added first.🤖 Generated with Claude Code