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
1 change: 1 addition & 0 deletions docs/src/content/docs/status.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Status of all agentic workflows. [Browse source files](https://github.com/github
| [Smoke Copilot](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/smoke-copilot.md) | copilot | [![Smoke Copilot](https://github.com/githubnext/gh-aw/actions/workflows/smoke-copilot.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/smoke-copilot.lock.yml) | `0 0,6,12,18 * * *` | - |
| [Smoke Detector - Smoke Test Failure Investigator](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/smoke-detector.md) | claude | [![Smoke Detector - Smoke Test Failure Investigator](https://github.com/githubnext/gh-aw/actions/workflows/smoke-detector.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/smoke-detector.lock.yml) | - | - |
| [Static Analysis Report](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/static-analysis-report.md) | claude | [![Static Analysis Report](https://github.com/githubnext/gh-aw/actions/workflows/static-analysis-report.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/static-analysis-report.lock.yml) | `0 9 * * *` | - |
| [Super Linter Report](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/super-linter-report.md) | copilot | [![Super Linter Report](https://github.com/githubnext/gh-aw/actions/workflows/super-linter-report.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/super-linter-report.lock.yml) | `0 14 * * 1-5` | - |
| [Technical Doc Writer](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/technical-doc-writer.md) | copilot | [![Technical Doc Writer](https://github.com/githubnext/gh-aw/actions/workflows/technical-doc-writer.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/technical-doc-writer.lock.yml) | - | - |
| [Test jqschema](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/test-jqschema.md) | copilot | [![Test jqschema](https://github.com/githubnext/gh-aw/actions/workflows/test-jqschema.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/test-jqschema.lock.yml) | - | - |
| [Test Manual Approval Workflow](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/test-manual-approval.md) | copilot | [![Test Manual Approval Workflow](https://github.com/githubnext/gh-aw/actions/workflows/test-manual-approval.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/test-manual-approval.lock.yml) | - | - |
Expand Down
119 changes: 119 additions & 0 deletions docs/src/styles/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -1347,3 +1347,122 @@ footer *,
max-width: none;
margin: 1.5rem 0;
}

/* Mobile-specific styles for screens up to 768px (tablets and phones) */
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment mentions 'tablets and phones' but the breakpoint at 768px may not cover all tablets. The test suite includes a 'Tablet 4:3 (iPad)' with width 1024px which would not match this media query. Consider updating the comment to clarify this only targets smaller phones and small tablets, or adjust the breakpoint if iPad-sized tablets should be included.

Suggested change
/* Mobile-specific styles for screens up to 768px (tablets and phones) */
/* Mobile-specific styles for screens up to 768px (phones and small tablets only) */

Copilot uses AI. Check for mistakes.
@media (max-width: 768px) {
/* Remove excessive left padding on mobile */
.page {
padding-left: 0 !important;
padding-right: 0 !important;
padding-top: 4rem !important;
}

/* Adjust header for mobile */
header {
height: auto;
min-height: 56px;
}

header .header {
height: auto;
min-height: 56px;
padding: 0.5rem 1rem;
}

/* Hide sidebar on mobile (it's already handled by Starlight) */
#starlight__sidebar,
.sidebar {
display: none;
}

/* Adjust main frame for mobile */
.main-frame {
max-width: 100%;
padding: 0 !important;
}

/* Content panel adjustments for mobile */
.content-panel {
padding: 0 1rem 2.5rem 1rem !important;
}

/* Hide right sidebar (TOC) on mobile */
.right-sidebar {
display: none;
}

/* Hero adjustments for mobile */
.hero {
padding: 2rem 1rem 1rem;
}

.hero h1 {
font-size: clamp(1.5rem, 6vw, 2rem);
}

.hero .tagline {
font-size: clamp(0.875rem, 3vw, 1rem);
}

/* Markdown content adjustments for mobile */
.sl-markdown-content {
max-width: 100% !important;
padding: 0;
}

.sl-markdown-content h1 {
font-size: 1.75rem;
}

.sl-markdown-content h2 {
font-size: 1.375rem;
margin-top: 2rem;
}

.sl-markdown-content h3 {
font-size: 1.125rem;
margin-top: 1.5rem;
}

/* Code blocks - prevent overflow */
.sl-markdown-content pre {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}

/* Tables - make them scrollable on mobile */
.sl-markdown-content table {
display: block;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}

/* Adjust site title for mobile */
.site-title {
font-size: 0.875rem;
}

.site-title img {
height: 1rem;
}

/* Pagination links adjustments for mobile */
.pagination-links {
flex-direction: column;
gap: 0.75rem;
}

.pagination-links a {
flex: 1 1 100%;
}

/* Ensure body layout doesn't break on mobile */
body .page .main-frame {
flex: 1 1 100%;
max-width: 100%;
}

body .page .main-frame > main {
width: 100%;
}
}
99 changes: 99 additions & 0 deletions docs/test-all-form-factors.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { chromium } from 'playwright';

(async () => {
const browser = await chromium.launch({
executablePath: '/usr/bin/chromium-browser',
headless: true,
});

// Define form factors to test
const formFactors = [
{
name: 'iPhone 16 (Mobile)',
viewport: { width: 393, height: 852 },
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1'
},
{
name: 'Desktop Portrait',
viewport: { width: 1080, height: 1920 }, // 9:16 aspect ratio
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false,
},
{
name: 'Desktop Landscape',
viewport: { width: 1920, height: 1080 }, // 16:9 aspect ratio
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false,
},
{
name: 'Tablet 4:3',
viewport: { width: 1024, height: 768 }, // 4:3 aspect ratio (iPad)
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
userAgent: 'Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1'
},
];

const pages = [
{ url: 'http://localhost:4321/gh-aw/', name: 'homepage' },
{ url: 'http://localhost:4321/gh-aw/get-started/about/', name: 'content' },
];

for (const formFactor of formFactors) {
console.log(`\n=== Testing: ${formFactor.name} ===`);

const context = await browser.newContext({
viewport: formFactor.viewport,
deviceScaleFactor: formFactor.deviceScaleFactor,
isMobile: formFactor.isMobile,
hasTouch: formFactor.hasTouch,
userAgent: formFactor.userAgent,
});

const page = await context.newPage();

for (const testPage of pages) {
console.log(`Loading ${testPage.name}...`);
await page.goto(testPage.url, { waitUntil: 'networkidle' });

// Calculate crop height to maintain 9:16 aspect ratio max
const { width, height } = formFactor.viewport;
const maxAspectRatio = 9 / 16; // Height/Width
const currentAspectRatio = height / width;

let cropHeight = height;
if (currentAspectRatio > maxAspectRatio) {
// Crop to 9:16
cropHeight = Math.floor(width * maxAspectRatio);
}

const filename = `/tmp/${formFactor.name.toLowerCase().replace(/\s+/g, '-')}-${testPage.name}.png`;

// Take screenshot with crop if needed
if (cropHeight < height) {
await page.screenshot({
path: filename,
clip: { x: 0, y: 0, width: width, height: cropHeight }
});
console.log(` Cropped screenshot (${width}x${cropHeight}) saved to ${filename}`);
} else {
await page.screenshot({
path: filename,
fullPage: false
});
console.log(` Screenshot (${width}x${height}) saved to ${filename}`);
}
}

await context.close();
}

await browser.close();
console.log('\n✓ All form factors tested!');
})();
40 changes: 40 additions & 0 deletions docs/test-mobile.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { chromium, devices } from 'playwright';
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The devices import is unused. It should be removed to avoid importing unnecessary dependencies.

Suggested change
import { chromium, devices } from 'playwright';
import { chromium } from 'playwright';

Copilot uses AI. Check for mistakes.

(async () => {
// Use the system chromium browser
const browser = await chromium.launch({
executablePath: '/usr/bin/chromium-browser',
headless: true,
});

// iPhone 16 has dimensions 393x852 (portrait)
const iPhone16 = {
name: 'iPhone 16',
viewport: { width: 393, height: 852 },
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1'
};

const context = await browser.newContext({
...iPhone16,
});

const page = await context.newPage();

// Test homepage
console.log('Loading homepage...');
await page.goto('http://localhost:4321/gh-aw/', { waitUntil: 'networkidle' });
await page.screenshot({ path: '/tmp/mobile-homepage.png', fullPage: true });
console.log('Homepage screenshot saved to /tmp/mobile-homepage.png');

// Test a content page
console.log('Loading get started page...');
await page.goto('http://localhost:4321/gh-aw/get-started/quickstart/', { waitUntil: 'networkidle' });
await page.screenshot({ path: '/tmp/mobile-content.png', fullPage: true });
console.log('Content page screenshot saved to /tmp/mobile-content.png');

await browser.close();
console.log('Done!');
})();
64 changes: 64 additions & 0 deletions docs/tests/mobile-responsive.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { test, expect } from '@playwright/test';

test.describe('Mobile and Responsive Layout', () => {
const formFactors = [
{ name: 'iPhone 16 (Mobile)', width: 393, height: 852 },
{ name: 'Tablet 4:3 (iPad)', width: 1024, height: 768 },
{ name: 'Desktop Portrait', width: 1080, height: 1920 },
{ name: 'Desktop Landscape', width: 1920, height: 1080 },
];

const pages = [
{ url: '/gh-aw/', name: 'homepage' },
{ url: '/gh-aw/get-started/about/', name: 'content page' },
];

for (const formFactor of formFactors) {
test.describe(`${formFactor.name}`, () => {
test.beforeEach(async ({ page }) => {
await page.setViewportSize({
width: formFactor.width,
height: formFactor.height
});
});

for (const testPage of pages) {
test(`should render ${testPage.name} correctly`, async ({ page }) => {
await page.goto(testPage.url);
await page.waitForLoadState('networkidle');

// Verify page loads
await expect(page).toHaveTitle(/GitHub Agentic Workflows/);

// Verify header is visible
const header = page.locator('header');
await expect(header).toBeVisible();

// Verify main content is visible
const main = page.locator('main');
await expect(main).toBeVisible();

// Check for horizontal scrollbar (should not exist)
const bodyScrollWidth = await page.evaluate(() => document.body.scrollWidth);
const bodyClientWidth = await page.evaluate(() => document.body.clientWidth);
expect(bodyScrollWidth).toBeLessThanOrEqual(bodyClientWidth + 1); // Allow 1px tolerance
});
}

test('should have proper content spacing on mobile', async ({ page }) => {
if (formFactor.width <= 768) {
await page.goto('/gh-aw/get-started/about/');
await page.waitForLoadState('networkidle');

// Content should have proper padding
const contentPanel = page.locator('.content-panel');
await expect(contentPanel).toBeVisible();

// Sidebar should be hidden on mobile
const sidebar = page.locator('.sidebar');
await expect(sidebar).not.toBeVisible();
}
});
});
}
});
Loading