feat(templates): fullstack mobile-first PWA manifest + mobile Playwright + axe

This commit is contained in:
opencode-agent 2026-08-05 03:34:31 +00:00
parent e4ddbb38af
commit f0d71c3dd3
7 changed files with 44 additions and 1 deletions

View file

@ -30,6 +30,7 @@
"@sveltejs/adapter-node": "^5.0.0", "@sveltejs/adapter-node": "^5.0.0",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0",
"@axe-core/playwright": "^4.10.0",
"@biomejs/biome": "^1.9.0", "@biomejs/biome": "^1.9.0",
"knip": "^5.30.0", "knip": "^5.30.0",
"svelte": "^5.0.0", "svelte": "^5.0.0",

View file

@ -12,6 +12,7 @@ export default defineConfig({
}, },
projects: [ projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }, { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'mobile-chrome', use: { ...devices['iPhone SE'], isMobile: true, hasTouch: true } },
], ],
webServer: { webServer: {
command: 'npm run preview', command: 'npm run preview',

View file

@ -2,8 +2,11 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" /> <link rel="icon" href="%sveltekit.assets%/icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="manifest" href="%sveltekit.assets%/manifest.webmanifest" />
<meta name="theme-color" content="#0f172a" />
<link rel="apple-touch-icon" href="%sveltekit.assets%/icon.svg" />
%sveltekit.head% %sveltekit.head%
</head> </head>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover">

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 192" width="192" height="192">
<rect width="192" height="192" rx="32" fill="#0f172a" />
<text x="96" y="128" font-family="system-ui, -apple-system, sans-serif" font-size="112" font-weight="700" fill="#f8fafc" text-anchor="middle">{{ cookiecutter.project_name | first | upper }}</text>
</svg>

After

Width:  |  Height:  |  Size: 352 B

View file

@ -0,0 +1,17 @@
{
"name": "{{ cookiecutter.project_name }}",
"short_name": "{{ cookiecutter.project_name }}",
"description": "{{ cookiecutter.description }}",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0f172a",
"icons": [
{
"src": "/icon.svg",
"sizes": "any",
"type": "image/svg+xml",
"purpose": "any maskable"
}
]
}

View file

@ -0,0 +1,10 @@
import { expect, test } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('dashboard has no a11y violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.disableRules(['color-contrast'])
.analyze();
expect(results.violations).toEqual([]);
});

View file

@ -0,0 +1,7 @@
import { expect, test } from '@playwright/test';
test('sidebar collapses on mobile viewport', async ({ page }) => {
await page.goto('/');
await expect(page.locator('[data-sidebar="sidebar"]')).not.toBeVisible();
await expect(page.locator('[data-sidebar="trigger"]')).toBeVisible();
});