From cccdc64786b1bf76f4870c969efe37ba918e5ee3 Mon Sep 17 00:00:00 2001 From: Mara Nikola Kiefer Date: Tue, 21 Jul 2026 10:14:34 +0200 Subject: [PATCH 1/2] Add theme-aware image guidelines and update screenshot generator for light/dark variants --- .github/workflows/guidelines.md | 34 +++ .../workshop-explanatory-diagrams.md | 47 +++- .github/workflows/workshop-ui-screenshots.md | 200 +++++++++++++----- 3 files changed, 223 insertions(+), 58 deletions(-) diff --git a/.github/workflows/guidelines.md b/.github/workflows/guidelines.md index 625da699..462aa87f 100644 --- a/.github/workflows/guidelines.md +++ b/.github/workflows/guidelines.md @@ -53,6 +53,40 @@ Use these rules across workshop authoring/editing workflows to keep the tutorial - Do **not** number Markdown headers inside a file. Use descriptive headings such as `### Open the Codespace`, not `### 2. Open the Codespace`. - Keep ordering in ordered lists, tables, filenames, and checkpoint lists instead of in the heading text itself. +## Theme-aware workshop images + +When Playwright or another automation creates a workshop screenshot or diagram, +generate both light and dark variants during the same run. Name them +`-light.` and `-dark.`, then use GitHub's +[theme-aware `` pattern](https://github.blog/developer-skills/github/how-to-make-your-images-in-markdown-on-github-adjust-for-dark-mode-and-light-mode/#one-snippet-two-themes): + +```html + + + + Concise descriptive alt text + +``` + +- Keep the fallback `` and use the light variant as its `src`. +- Put alt text on the fallback ``; do not duplicate it on `` elements. +- Capture or render each variant with Playwright's matching `colorScheme` setting. +- Verify in both color schemes that `currentSrc` selects the expected variant and + that the image is nonblank, readable, and free of clipping or overflow. + +Existing single-theme workshop images are migration candidates, not exceptions: + +- Inventory Markdown image references and HTML `src`/`srcset` references so + existing `` blocks remain auditable. +- Migrate at most three existing visuals per automated pull request. Process core + workshop pages before setup paths, advanced topics, and side quests. +- Preserve each visual's stem and alt text while creating `-light` and `-dark` + variants and replacing the original reference with the `` pattern. +- Create a genuine theme-specific variant for UI screenshots and diagrams. A + theme-neutral photo may use the same existing file for both `` entries + instead of duplicating the binary. +- Do not delete an original asset until no Markdown or HTML reference uses it. + ## Alert callouts: use `
` only for multi-line content ### Alert level ceiling diff --git a/.github/workflows/workshop-explanatory-diagrams.md b/.github/workflows/workshop-explanatory-diagrams.md index a584e121..7b196f6c 100644 --- a/.github/workflows/workshop-explanatory-diagrams.md +++ b/.github/workflows/workshop-explanatory-diagrams.md @@ -3,8 +3,8 @@ emoji: 🧠 name: Workshop Explanatory Diagram Generator description: > Daily workshop illustrator that finds one concept in the workshop worth - explaining visually, generates a polished educational SVG diagram, and opens a - PR with the new image and minimal markdown updates. + explaining visually, generates or migrates a polished light/dark SVG diagram + pair, and opens a PR with theme-aware markdown updates. on: schedule: daily workflow_dispatch: @@ -64,6 +64,8 @@ steps: ) image_re = re.compile(r'!\[[^\]]*\]\(([^)]+)\)') + picture_re = re.compile(r'', re.IGNORECASE | re.DOTALL) + html_image_re = re.compile(r'\b(?:src|srcset)="([^"]+)"') heading_re = re.compile(r'^(##+)\s+(.*)$', re.MULTILINE) file_summaries = [] @@ -74,11 +76,24 @@ steps: (line[2:].strip() for line in lines if line.startswith("# ")), path.stem, ) - images = [ + markdown_images = [ match.group(1) for match in image_re.finditer(text) if not match.group(1).startswith(("http://", "https://")) ] + picture_blocks = picture_re.findall(text) + picture_images = [ + match.group(1) + for block in picture_blocks + for match in html_image_re.finditer(block) + if not match.group(1).startswith(("http://", "https://")) + ] + images = list(dict.fromkeys(markdown_images + picture_images)) + theme_aware_picture_count = sum( + '(prefers-color-scheme: light)' in block + and '(prefers-color-scheme: dark)' in block + for block in picture_blocks + ) headings = [ { "level": len(match.group(1)), @@ -95,6 +110,8 @@ steps: "word_count": len(re.findall(r"\S+", text)), "image_count": len(images), "images": images, + "single_theme_images": markdown_images, + "theme_aware_picture_count": theme_aware_picture_count, "headings": headings, } ) @@ -125,9 +142,9 @@ You are an educational illustrator for the **Learning GitHub Agentic Workflows** workshop. Your job is to find exactly one workshop concept that would be easier to learn -with a visual explanation, turn that concept into a polished SVG diagram, and -open a draft pull request with the image and the smallest markdown edit needed to -use it. +with a visual explanation, or one existing single-theme explanatory diagram, +turn it into a polished light/dark SVG pair, and open a draft pull request with +the smallest Markdown edit needed to use it. Create **at most one** pull request per run. @@ -153,10 +170,22 @@ Create **at most one** pull request per run. --- -## Pick one concept worth illustrating +## Pick One Migration or Concept + +Inspect the selected file's `single_theme_images` first: + +1. Classify each existing image as an explanatory diagram, GitHub UI screenshot, + or theme-neutral photo. +2. If an explanatory diagram exists, select exactly one for migration and do not + create a new concept in this run. +3. Leave GitHub UI screenshots to `workshop-ui-screenshots`. +4. A theme-neutral photo may be converted to a `` block that uses the + same file for both theme sources; do not duplicate its binary. + +If no existing image in the selected file needs migration, choose exactly one +new concept worth illustrating. -Choose exactly one concept from the selected file that benefits from a conceptual -visual sketch. +Choose a concept that benefits from a conceptual visual sketch. Good candidates: diff --git a/.github/workflows/workshop-ui-screenshots.md b/.github/workflows/workshop-ui-screenshots.md index 61840b93..12e5096b 100644 --- a/.github/workflows/workshop-ui-screenshots.md +++ b/.github/workflows/workshop-ui-screenshots.md @@ -3,9 +3,9 @@ emoji: 🖼️ name: Workshop UI Screenshot Generator description: > Daily scanner that finds image references in workshop markdown files, checks - existing images for broken links, and generates conceptual SVG illustrations - for missing GitHub UI screenshots. Files a PR with new SVGs and updates - markdown references. Reports remaining broken links as issues. + existing images for broken links, and generates paired light/dark conceptual + SVG illustrations for missing or single-theme GitHub UI screenshots. Files a + PR with new SVGs and theme-aware references. Reports remaining broken links. on: schedule: daily workflow_dispatch: @@ -85,6 +85,70 @@ steps: echo "[]" > /tmp/gh-aw/data/image-refs.json fi + # Add local src/srcset references from HTML blocks. The Markdown + # scanner above remains responsible for ![alt](path) references. + python3 <<'PY' + import json + import pathlib + from html.parser import HTMLParser + + refs_path = pathlib.Path("/tmp/gh-aw/data/image-refs.json") + refs = json.loads(refs_path.read_text()) + seen = {(record["file"], record["path"]) for record in refs} + + def add_ref(source_file, line, alt, image_path): + image_path = image_path.strip() + if not image_path or image_path.startswith(("http://", "https://", "data:")): + return + key = (str(source_file), image_path) + if key in seen: + return + seen.add(key) + resolved = (source_file.parent / image_path).resolve() + refs.append({ + "file": str(source_file), + "line": line, + "alt": alt, + "path": image_path, + "resolved": str(resolved), + "exists": resolved.is_file(), + }) + + class PictureParser(HTMLParser): + def __init__(self, source_file): + super().__init__() + self.source_file = source_file + self.picture_refs = None + + def handle_starttag(self, tag, attrs): + attrs = dict(attrs) + if tag == "picture": + self.picture_refs = [] + elif tag == "source" and self.picture_refs is not None: + for candidate in attrs.get("srcset", "").split(","): + candidate = candidate.strip().split()[0] if candidate.strip() else "" + if candidate: + self.picture_refs.append((self.getpos()[0], "", candidate)) + elif tag == "img" and self.picture_refs is not None and attrs.get("src"): + self.picture_refs.append( + (self.getpos()[0], attrs.get("alt", ""), attrs["src"]) + ) + + def handle_endtag(self, tag): + if tag != "picture" or self.picture_refs is None: + return + fallback_alt = next((alt for _, alt, _ in self.picture_refs if alt), "") + for line, alt, image_path in self.picture_refs: + add_ref(self.source_file, line, alt or fallback_alt, image_path) + self.picture_refs = None + + for source_file in sorted(pathlib.Path("workshop").glob("*.md")): + parser = PictureParser(source_file) + parser.feed(source_file.read_text(encoding="utf-8")) + + refs_path.write_text(json.dumps(refs, indent=2), encoding="utf-8") + PY + echo "$existing_images" > /tmp/gh-aw/data/existing-images.json echo "=== Existing images ===" && cat /tmp/gh-aw/data/existing-images.json @@ -94,9 +158,9 @@ steps: # Workshop UI Screenshot Generator You are an image-quality automation agent for the **"Learning GitHub Agentic -Workflows"** workshop. Your job is to find broken image references, generate -conceptual SVG screenshots for missing GitHub UI images, and report anything -that still needs attention. +Workflows"** workshop. Your job is to find broken or single-theme image +references, generate paired light/dark conceptual SVG screenshots for GitHub UI +images, and report anything that still needs attention. --- @@ -114,53 +178,70 @@ that still needs attention. 2. Read `/tmp/gh-aw/data/existing-images.json` — the list of existing files under `workshop/images/`. -3. Partition image references into two groups: +3. Partition image references into three groups: - **broken** — records where `exists` is `false` - - **ok** — records where `exists` is `true` + - **theme-aware** — valid references already inside a `` block with + both `-light` and `-dark` variants + - **single-theme** — valid Markdown image references that do not have a + corresponding light/dark `` pair + +4. From **single-theme**, identify existing GitHub UI screenshots using the same + filename/alt-text criteria as broken references. Sort them by workshop + adventure priority: core, setup, advanced, then side quests. Select at most + three migration candidates per run. -4. If **broken** is empty, call `noop` with: - `Scanned N markdown files. All M image references are valid — no broken links found.` +5. If **broken** and the selected migration candidates are both empty, call + `noop` with: + `Scanned N markdown files. No broken links or pending GitHub UI theme migrations found.` --- -## Classify Broken References +## Classify References -For each broken reference, determine whether it depicts a **GitHub UI element** -that can be illustrated with a conceptual SVG. +For each broken reference and selected migration candidate, determine whether it +depicts a **GitHub UI element** that can be illustrated with a conceptual SVG. A reference is classifiable as a GitHub UI screenshot when: - Its filename or alt text mentions common GitHub UI elements such as: Actions tab, workflow run, Run workflow button, Codespace, fork, commit, schedule badge, skipped step, summary panel, log view, or similar. -Classify each broken reference as: +Classify each reference as: - `generatable` — can be illustrated with a conceptual SVG - `not-generatable` — too specific, a real screenshot is needed (e.g. a photo of a custom user screen or non-GitHub UI) --- -## Generate Conceptual SVGs +## Generate Theme-Aware SVGs -For each `generatable` broken reference, generate a conceptual SVG that -illustrates the described GitHub UI element. +For each `generatable` broken reference or migration candidate, generate a +light/dark SVG pair that illustrates the described GitHub UI element. If either +member of an existing pair is broken, repair or regenerate both variants as one +change set. ### SVG design rules -- **Canvas**: 1200 x 560, `viewBox="0 0 1200 560"`. -- **Background**: `#f6f8fa` (GitHub light page background). -- Use a browser chrome header (`#ffffff`, height 44, with rounded top corners, - subtle `#d0d7de` border) showing a mocked URL bar containing the workshop +- **Canvas**: 1200 x 560, `viewBox="0 0 1200 560"` for both variants. +- Keep geometry, labels, and content identical across the pair; change only + theme-dependent colors. +- Light palette: page `#f6f8fa`, panels `#ffffff`, border `#d0d7de`, primary + text `#24292f`, muted text `#57606a`, and accent `#0969da`. +- Dark palette: page `#0d1117`, panels `#161b22`, border `#30363d`, primary + text `#f0f6fc`, muted text `#8b949e`, and accent `#2f81f7`. +- Use a browser chrome header (height 44, rounded top corners, and the palette's + panel and border colors) showing a mocked URL bar containing the workshop repo path. - Represent GitHub's navigation tabs (Code, Issues, Pull requests, Actions, - etc.) as a horizontal tab bar with the relevant tab underlined in `#0969da`. + etc.) as a horizontal tab bar with the relevant tab underlined in the palette + accent color. - For Actions-tab screenshots: show a workflow list panel with a single row representing the relevant workflow, a status icon (green ✓ for success, yellow (in-progress hourglass) for in-progress), and the workflow name. - For Run workflow button: render a blue button labelled "Run workflow" in the Actions sidebar. -- For Codespace/fork/commit dialogs: use a centered modal panel (`#ffffff` - background, `#d0d7de` border, rounded corners). +- For Codespace/fork/commit dialogs: use a centered modal panel with the + palette's panel background and border colors. - For schedule badge / workflow list badge: render the Actions sidebar list with a clock icon and "Scheduled" label. - For skipped steps: render the job-step list with a grey `-` icon and @@ -168,43 +249,61 @@ illustrates the described GitHub UI element. - For summary/run log panels: render a dark panel (`#0d1117`) with monospace output lines in `#c9d1d9`, showing representative output from the described step. -- Add a short annotation label below the graphic (in `#57606a`, 24 px, - `Arial, sans-serif`) echoing the alt text. +- Add a short annotation label below the graphic (using the palette's muted text + color, 24 px, `Arial, sans-serif`) echoing the alt text. - Add `role="img"` and `aria-label` matching the alt text. ### Naming convention For a broken reference `images/08-run-summary.png`: -- Generate the SVG as `workshop/images/08-run-summary.svg`. +- Generate `workshop/images/08-run-summary-light.svg` and + `workshop/images/08-run-summary-dark.svg`. - Do **not** create a PNG; always produce an SVG. -Write each SVG to the appropriate path using the `edit` tool (or create it -fresh if the path does not exist). +For an existing single-theme SVG, preserve its stem when naming the pair. Do not +overwrite the original until all references have moved to the pair. + +Write each SVG pair to the appropriate paths using the `edit` tool (or create +them fresh if the paths do not exist). ### Markdown reference update -After writing each new SVG file, update the corresponding line in the source -markdown file to reference `.svg` instead of the original extension. +After writing each pair, replace the corresponding Markdown image line with +GitHub's theme-aware `` pattern: + +```html + + + + Workflow run summary panel + +``` -Example: Replace `images/08-run-summary.png` with `images/08-run-summary.svg` -in the exact line where the reference appears. +Use the original alt text on the fallback ``. Keep the light variant as the +fallback `src` and do not put alt text on `` elements. When repairing an +existing `` block, update the whole block and preserve its fallback alt +text. --- -## Render and QA generated SVGs +## Render and QA Generated SVGs -If you generated any SVGs in Phase 3: +If you generated any SVG pairs: 1. Start a local static file server from the repository root so `workshop/images/` is reachable on `http://127.0.0.1`. -2. Use Playwright to open each newly generated SVG in a browser tab at least once. -3. Inspect the rendered output for text bleeding outside buttons, tabs, chips, +2. Use Playwright with `colorScheme: "light"` to render the exact `` + block and confirm its `currentSrc` ends with `-light.svg`. +3. Repeat with `colorScheme: "dark"` and confirm `currentSrc` ends with + `-dark.svg`. +4. Capture and inspect both rendered variants for text bleeding outside buttons, + tabs, chips, dialogs, panels, annotations, or other bounding shapes. -4. If any text bleeds out of its box, fix the SVG before continuing by widening - the container, wrapping or shortening the label, moving nearby elements, or - reducing font size only as much as needed. -5. Re-render the updated SVG with Playwright and repeat until every generated - SVG is visually contained and readable. +5. If any text bleeds out of its box, fix both SVGs before continuing by + widening the container, wrapping or shortening the label, moving nearby + elements, or reducing font size only as much as needed. +6. Re-render both SVGs and repeat until each image is nonblank, visually + contained, readable, and selected in the intended theme. Treat the render check as required final QA, not an optional spot check. @@ -234,27 +333,30 @@ For each `not-generatable` broken reference (real screenshot needed): ## Open a Pull Request -If any SVG files were generated in Phase 3: +If any SVG pairs were generated: Call `create-pull-request` with: -- **Title**: `Add conceptual SVG screenshots for N missing workshop images` +- **Title**: `Add theme-aware SVG screenshots for N workshop images` - **Body** summarising: - - Which images were generated (list by filename) + - Which light/dark pairs were generated (list both filenames) + - Which changes repair broken links and which migrate existing images - Which markdown files were updated - A note that these are AI-generated conceptual illustrations; maintainers should replace them with real screenshots when available -If no SVGs were generated (only `not-generatable` references exist), skip +If no SVG pairs were generated (only `not-generatable` references exist), skip `create-pull-request`. --- ## Output quality requirements -- Every generated SVG must be valid, self-contained SVG markup (start with +- Both generated SVGs must be valid, self-contained SVG markup (start with `` block with a light fallback. - Markdown updates must be minimal line-precise replacements — do not reformat surrounding content. - Never call write tools other than `create-pull-request`, `create-issue`, and From 625d40dd7d2156dcb1865ce09a310b7c4158f2bb Mon Sep 17 00:00:00 2001 From: Mara Nikola Kiefer Date: Tue, 21 Jul 2026 10:16:56 +0200 Subject: [PATCH 2/2] Update diagram generation to support light/dark SVG pairs --- .../workshop-explanatory-diagrams.md | 97 +++++++++++-------- 1 file changed, 56 insertions(+), 41 deletions(-) diff --git a/.github/workflows/workshop-explanatory-diagrams.md b/.github/workflows/workshop-explanatory-diagrams.md index 7b196f6c..efcb3e37 100644 --- a/.github/workflows/workshop-explanatory-diagrams.md +++ b/.github/workflows/workshop-explanatory-diagrams.md @@ -213,24 +213,27 @@ Prefer concepts that are: --- -## Create one SVG explanatory diagram +## Create One Theme-Aware Diagram -Generate exactly one SVG file for the chosen concept. +Generate exactly one light/dark SVG pair for the chosen concept or migration. +For a migration, preserve the original content, geometry, stem, and alt text. ### SVG style rules Match the visual family of the UI screenshot workflow while staying conceptual: -- Canvas: `1200 x 560`, `viewBox="0 0 1200 560"` -- Background: `#f6f8fa` -- Main diagram card: white panel with rounded corners and a subtle `#d0d7de` - border -- Use GitHub-aligned colors such as `#0969da`, `#1a7f37`, `#8250df`, `#bf8700`, - `#cf222e`, `#57606a`, and `#24292f` +- Canvas: `1200 x 560`, `viewBox="0 0 1200 560"` for both variants +- Keep geometry, labels, and content identical across the pair +- Light palette: background `#f6f8fa`, panel `#ffffff`, border `#d0d7de`, + primary text `#24292f`, muted text `#57606a`, and accent `#0969da` +- Dark palette: background `#0d1117`, panel `#161b22`, border `#30363d`, + primary text `#f0f6fc`, muted text `#8b949e`, and accent `#2f81f7` +- Adapt semantic success, attention, danger, and done colors for sufficient + contrast in each theme - Use simple labeled boxes, arrows, chips, dashed groupings, and numbered badges - Keep labels short and learner-friendly - Add a short title inside the graphic and a short annotation below it -- Add `role="img"` and `aria-label` matching the markdown alt text +- Add `role="img"` and `aria-label` matching the Markdown alt text - Output valid, self-contained SVG only ### Diagram content rules @@ -244,54 +247,65 @@ Match the visual family of the UI screenshot workflow while staying conceptual: ### Naming convention -Write the file under `workshop/images/` using the workshop step identifier plus a -short concept slug. +Write both files under `workshop/images/` using the workshop step identifier plus +a short concept slug and theme suffix. Examples: - `workshop/13-schedule-it.md` + concept `schedule loop` → - `workshop/images/13-schedule-loop.svg` + `workshop/images/13-schedule-loop-light.svg` and + `workshop/images/13-schedule-loop-dark.svg` - `workshop/16-connect-data-source.md` + concept `step-to-agent flow` → - `workshop/images/16-step-agent-flow.svg` + `workshop/images/16-step-agent-flow-light.svg` and + `workshop/images/16-step-agent-flow-dark.svg` If the first filename choice already exists for another concept, choose a more specific slug instead of overwriting unrelated work. --- -## Update markdown minimally +## Update Markdown Minimally -After generating the SVG: +After generating the SVG pair: -1. Insert exactly one markdown image reference in the source workshop file near - the concept it explains. +1. Insert or replace exactly one image reference in the source workshop file + near the concept it explains. 2. Use this format exactly: - ```markdown - ![Concise descriptive alt text](images/.svg) - ``` +```html + + + + Concise descriptive alt text + +``` -3. Keep the edit minimal and line-precise. -4. If needed, add at most two short sentences to introduce the diagram. -5. Do not reformat surrounding sections. -6. Do not add more than one image per run. +1. Use the light variant as the fallback `src`; put alt text only on ``. +2. Keep the edit minimal and line-precise. +3. If needed, add at most two short sentences to introduce a new diagram. +4. Do not reformat surrounding sections or migrate more than one image per run. +5. Do not delete the original until no Markdown or HTML reference uses it. --- -## Render and QA the diagram +## Render and QA the Diagram -After generating the SVG and updating markdown: +After generating the SVG pair and updating Markdown: 1. Start a local static file server from the repository root so the new - `workshop/images/*.svg` file is reachable on `http://127.0.0.1`. -2. Use Playwright to render the new SVG in a browser tab. -3. Check that all text stays inside its boxes, chips, badges, callouts, and + `workshop/images/*.svg` files are reachable on `http://127.0.0.1`. +2. Use Playwright with `colorScheme: "light"` to render the exact `` + block and confirm its `currentSrc` ends with `-light.svg`. +3. Repeat with `colorScheme: "dark"` and confirm `currentSrc` ends with + `-dark.svg`. +4. Capture both variants and check that the images are nonblank and all text + stays inside its boxes, chips, badges, callouts, and other visual containers at normal browser zoom. -4. If any label bleeds out of a box, revise the SVG before continuing by - widening the shape, wrapping or shortening the text, adjusting spacing, or - slightly reducing font size. -5. Re-render after each fix until the final image is visually clean and easy to - read. +5. If any label bleeds out of a box, revise both SVGs before continuing by + widening the shape, wrapping or shortening the text, adjusting spacing, or + slightly reducing font size. +6. Re-render after each fix until both variants are visually clean, readable, + and selected in the intended theme. Treat this render pass as required final QA for every generated diagram. @@ -299,13 +313,13 @@ Treat this render pass as required final QA for every generated diagram. ## Pull request requirements -If you generated a diagram, call `create-pull-request` with: +If you generated or migrated a diagram, call `create-pull-request` with: -- **Title**: `Add explanatory SVG diagram for ` +- **Title**: `Add theme-aware explanatory diagram for ` - **Body** summarizing: - which workshop file changed - - which concept was illustrated - - which SVG file was added + - whether the change creates a concept or migrates an existing diagram + - both SVG files added - why the diagram helps learners --- @@ -324,8 +338,9 @@ Call `noop` with a concise explanation when: ## Output quality requirements - Make only one coherent diagram change set per run. -- Keep the markdown edit smaller than the SVG addition. -- Ensure the final SVG passes a Playwright render check with no text bleeding - outside its visual containers. +- Keep the Markdown edit smaller than the SVG additions. +- Ensure both SVGs pass Playwright checks in their matching color schemes, + including expected `currentSrc`, nonblank pixels, and no text bleeding. +- Use a theme-aware `` block with the light variant as fallback. - Do not modify files outside `workshop/`. - Never use write tools other than `create-pull-request` and `noop`.