docs: update screenshots to reflect current UI state
Replace old screenshots with new ones showing updated dashboard design and weather widget. Update README to display mobile + desktop side-by-side, regenerate social preview images with new dashboard screenshot. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@@ -14,31 +14,30 @@
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" width="33%">
|
||||
<td align="center" width="50%">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/mobile-dark/mobile-dark-dashboard-2.png">
|
||||
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/mobile-light/mobile-light-dashboard-2.png">
|
||||
<img src="docs/screenshots/mobile-light/mobile-light-dashboard-2.png" alt="Dashboard" width="240">
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/01-mobile-dark.png">
|
||||
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/01-mobile-light.png">
|
||||
<img src="docs/screenshots/01-mobile-light.png" alt="Mobile Dashboard" width="280">
|
||||
</picture>
|
||||
<br>
|
||||
<sub>Mobile Dashboard</sub>
|
||||
</td>
|
||||
<td align="center" width="33%">
|
||||
<td align="center" width="50%">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/mobile-dark/mobile-dark-tasks-2.png">
|
||||
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/mobile-light/mobile-light-tasks-2.png">
|
||||
<img src="docs/screenshots/mobile-light/mobile-light-tasks-2.png" alt="Tasks" width="240">
|
||||
</picture>
|
||||
</td>
|
||||
<td align="center" width="33%">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/mobile-dark/mobile-dark-meal.png">
|
||||
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/mobile-light/mobile-light-meal.png">
|
||||
<img src="docs/screenshots/mobile-light/mobile-light-meal.png" alt="Meals" width="240">
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/01-web-dark.png">
|
||||
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/01-web-light.png">
|
||||
<img src="docs/screenshots/01-web-light.png" alt="Desktop Dashboard" width="480">
|
||||
</picture>
|
||||
<br>
|
||||
<sub>Desktop Dashboard</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center"><sub>Toggle GitHub light/dark mode to see both themes.</sub></p>
|
||||
<p align="center">
|
||||
<sub>Toggle GitHub light/dark mode to see both themes • <a href="https://ulsklyc.github.io/oikos/">View all screenshots</a></sub>
|
||||
</p>
|
||||
|
||||
Oikos is a self-hosted web app for families who want to organize their everyday life in one place — without cloud accounts, subscriptions, or data leaving the house. It runs as a Docker container on any home server or NAS, is accessible from every device in the household, and can be installed as a PWA on phones and tablets.
|
||||
|
||||
|
||||
@@ -651,35 +651,35 @@
|
||||
</div>
|
||||
<div class="screenshots-track" role="region" aria-label="Screenshots" tabindex="0">
|
||||
<div class="screenshot-item reveal">
|
||||
<img class="screenshot-img" data-light="screenshots/mobile-light/mobile-light-dashboard-2.png" data-dark="screenshots/mobile-dark/mobile-dark-dashboard-2.png" src="screenshots/mobile-light/mobile-light-dashboard-2.png" alt="Oikos Dashboard" width="270" height="480" loading="lazy">
|
||||
<img class="screenshot-img" data-light="screenshots/01-mobile-light.png" data-dark="screenshots/01-mobile-dark.png" src="screenshots/01-mobile-light.png" alt="Oikos Dashboard" width="270" height="480" loading="lazy">
|
||||
<p class="screenshot-label">Dashboard</p>
|
||||
</div>
|
||||
<div class="screenshot-item reveal reveal-delay-1">
|
||||
<img class="screenshot-img" data-light="screenshots/mobile-light/mobile-light-tasks-2.png" data-dark="screenshots/mobile-dark/mobile-dark-tasks-2.png" src="screenshots/mobile-light/mobile-light-tasks-2.png" alt="Task Management" width="270" height="480" loading="lazy">
|
||||
<img class="screenshot-img" data-light="screenshots/02-mobile-light.png" data-dark="screenshots/02-mobile-dark.png" src="screenshots/02-mobile-light.png" alt="Task Management" width="270" height="480" loading="lazy">
|
||||
<p class="screenshot-label" data-i18n="feat_tasks_title">Tasks</p>
|
||||
</div>
|
||||
<div class="screenshot-item reveal reveal-delay-2">
|
||||
<img class="screenshot-img" data-light="screenshots/mobile-light/mobile-light-meal.png" data-dark="screenshots/mobile-dark/mobile-dark-meal.png" src="screenshots/mobile-light/mobile-light-meal.png" alt="Meal Planning" width="270" height="480" loading="lazy">
|
||||
<img class="screenshot-img" data-light="screenshots/03-mobile-light.png" data-dark="screenshots/03-mobile-dark.png" src="screenshots/03-mobile-light.png" alt="Meal Planning" width="270" height="480" loading="lazy">
|
||||
<p class="screenshot-label" data-i18n="feat_meals_title">Meals</p>
|
||||
</div>
|
||||
<div class="screenshot-item reveal reveal-delay-3">
|
||||
<img class="screenshot-img" data-light="screenshots/mobile-light/mobile-light-shopping.png" data-dark="screenshots/mobile-dark/mobile-dark-shopping.png" src="screenshots/mobile-light/mobile-light-shopping.png" alt="Shopping Lists" width="270" height="480" loading="lazy">
|
||||
<img class="screenshot-img" data-light="screenshots/04-mobile-light.png" data-dark="screenshots/04-mobile-dark.png" src="screenshots/04-mobile-light.png" alt="Shopping Lists" width="270" height="480" loading="lazy">
|
||||
<p class="screenshot-label" data-i18n="feat_shopping_title">Shopping</p>
|
||||
</div>
|
||||
<div class="screenshot-item reveal">
|
||||
<img class="screenshot-img" data-light="screenshots/mobile-light/mobile-light-calendar.png" data-dark="screenshots/mobile-dark/mobile-dark-calendar.png" src="screenshots/mobile-light/mobile-light-calendar.png" alt="Calendar" width="270" height="480" loading="lazy">
|
||||
<img class="screenshot-img" data-light="screenshots/05-mobile-light.png" data-dark="screenshots/05-mobile-dark.png" src="screenshots/05-mobile-light.png" alt="Calendar" width="270" height="480" loading="lazy">
|
||||
<p class="screenshot-label" data-i18n="feat_calendar_title">Calendar</p>
|
||||
</div>
|
||||
<div class="screenshot-item reveal reveal-delay-1">
|
||||
<img class="screenshot-img" data-light="screenshots/mobile-light/mobile-light-budget-2.png" data-dark="screenshots/mobile-dark/mobile-dark-budget-2.png" src="screenshots/mobile-light/mobile-light-budget-2.png" alt="Budget Tracking" width="270" height="480" loading="lazy">
|
||||
<img class="screenshot-img" data-light="screenshots/06-mobile-light.png" data-dark="screenshots/06-mobile-dark.png" src="screenshots/06-mobile-light.png" alt="Budget Tracking" width="270" height="480" loading="lazy">
|
||||
<p class="screenshot-label" data-i18n="feat_budget_title">Budget</p>
|
||||
</div>
|
||||
<div class="screenshot-item reveal reveal-delay-2">
|
||||
<img class="screenshot-img" data-light="screenshots/mobile-light/mobile-light-notes-2.png" data-dark="screenshots/mobile-dark/mobile-dark-notes-2.png" src="screenshots/mobile-light/mobile-light-notes-2.png" alt="Notes" width="270" height="480" loading="lazy">
|
||||
<img class="screenshot-img" data-light="screenshots/07-mobile-light.png" data-dark="screenshots/07-mobile-dark.png" src="screenshots/07-mobile-light.png" alt="Notes" width="270" height="480" loading="lazy">
|
||||
<p class="screenshot-label" data-i18n="feat_notes_title">Notes</p>
|
||||
</div>
|
||||
<div class="screenshot-item reveal reveal-delay-3">
|
||||
<img class="screenshot-img" data-light="screenshots/mobile-light/mobile-light-contacts-2.png" data-dark="screenshots/mobile-dark/mobile-dark-contacts-2.png" src="screenshots/mobile-light/mobile-light-contacts-2.png" alt="Contacts" width="270" height="480" loading="lazy">
|
||||
<img class="screenshot-img" data-light="screenshots/08-mobile-light.png" data-dark="screenshots/08-mobile-dark.png" src="screenshots/08-mobile-light.png" alt="Contacts" width="270" height="480" loading="lazy">
|
||||
<p class="screenshot-label" data-i18n="feat_contacts_title">Contacts</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 344 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 940 KiB |
|
After Width: | Height: | Size: 940 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 971 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 954 KiB |
|
After Width: | Height: | Size: 946 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 992 KiB |
|
After Width: | Height: | Size: 987 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1008 KiB |
|
After Width: | Height: | Size: 1007 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1006 KiB |
|
After Width: | Height: | Size: 1001 KiB |
|
Before Width: | Height: | Size: 442 KiB |
|
Before Width: | Height: | Size: 379 KiB |
|
Before Width: | Height: | Size: 558 KiB |
|
Before Width: | Height: | Size: 582 KiB |
|
Before Width: | Height: | Size: 415 KiB |
|
Before Width: | Height: | Size: 540 KiB |
|
Before Width: | Height: | Size: 371 KiB |
|
Before Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 479 KiB |
|
Before Width: | Height: | Size: 449 KiB |
|
Before Width: | Height: | Size: 383 KiB |
|
Before Width: | Height: | Size: 559 KiB |
|
Before Width: | Height: | Size: 554 KiB |
|
Before Width: | Height: | Size: 434 KiB |
|
Before Width: | Height: | Size: 544 KiB |
|
Before Width: | Height: | Size: 381 KiB |
|
Before Width: | Height: | Size: 334 KiB |
|
Before Width: | Height: | Size: 499 KiB |
|
Before Width: | Height: | Size: 368 KiB |
|
Before Width: | Height: | Size: 347 KiB |
|
Before Width: | Height: | Size: 450 KiB |
|
Before Width: | Height: | Size: 583 KiB |
|
Before Width: | Height: | Size: 379 KiB |
|
Before Width: | Height: | Size: 507 KiB |
|
Before Width: | Height: | Size: 308 KiB |
|
Before Width: | Height: | Size: 276 KiB |
|
Before Width: | Height: | Size: 380 KiB |
|
Before Width: | Height: | Size: 367 KiB |
|
Before Width: | Height: | Size: 345 KiB |
|
Before Width: | Height: | Size: 447 KiB |
|
Before Width: | Height: | Size: 557 KiB |
|
Before Width: | Height: | Size: 393 KiB |
|
Before Width: | Height: | Size: 506 KiB |
|
Before Width: | Height: | Size: 311 KiB |
|
Before Width: | Height: | Size: 277 KiB |
|
Before Width: | Height: | Size: 391 KiB |
|
Before Width: | Height: | Size: 348 KiB After Width: | Height: | Size: 354 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 381 KiB |
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env node
|
||||
import sharp from 'sharp';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
const SCREENSHOT = 'docs/screenshots/01-web-light.png';
|
||||
const BG_COLOR = '#1A1A28';
|
||||
|
||||
// Social image dimensions
|
||||
const DIMENSIONS = {
|
||||
'og-image.png': { width: 1200, height: 630 },
|
||||
'twitter-image.png': { width: 1200, height: 675 },
|
||||
'social-preview.png': { width: 1280, height: 640 }
|
||||
};
|
||||
|
||||
async function createSocialImage(filename, width, height) {
|
||||
// Load and resize screenshot to fit right side (60% of width)
|
||||
const screenshotWidth = Math.floor(width * 0.55);
|
||||
const screenshotHeight = Math.floor(height * 0.7);
|
||||
|
||||
const screenshot = await sharp(SCREENSHOT)
|
||||
.resize(screenshotWidth, screenshotHeight, {
|
||||
fit: 'contain',
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||
})
|
||||
.toBuffer();
|
||||
|
||||
// Create SVG with text and layout
|
||||
const svg = `
|
||||
<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background -->
|
||||
<rect width="${width}" height="${height}" fill="${BG_COLOR}"/>
|
||||
|
||||
<!-- Left side content area -->
|
||||
<g transform="translate(60, ${height / 2 - 120})">
|
||||
<!-- Logo badge -->
|
||||
<rect x="0" y="0" width="160" height="28" rx="4" fill="#0A84FF" opacity="0.15"/>
|
||||
<text x="12" y="19" font-family="system-ui, -apple-system, sans-serif" font-size="12" font-weight="600" fill="#0A84FF" letter-spacing="0.5">
|
||||
SELF-HOSTED · OPEN SOURCE
|
||||
</text>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="0" y="80" font-family="system-ui, -apple-system, sans-serif" font-size="64" font-weight="700" fill="#FFFFFF">
|
||||
Oikos
|
||||
</text>
|
||||
|
||||
<!-- Description -->
|
||||
<text x="0" y="130" font-family="system-ui, -apple-system, sans-serif" font-size="18" fill="#B0B0B8" font-weight="400">
|
||||
<tspan x="0" dy="0">The family planner that respects your</tspan>
|
||||
<tspan x="0" dy="28">privacy. Tasks, calendars, shopping, meals,</tspan>
|
||||
<tspan x="0" dy="28">budget — on your own server.</tspan>
|
||||
</text>
|
||||
|
||||
<!-- Feature badges -->
|
||||
<g transform="translate(0, 240)">
|
||||
<g>
|
||||
<rect x="0" y="0" width="90" height="32" rx="6" fill="#2A2A38"/>
|
||||
<text x="12" y="21" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="500" fill="#8E8E93">✓ Tasks</text>
|
||||
</g>
|
||||
<g transform="translate(100, 0)">
|
||||
<rect x="0" y="0" width="110" height="32" rx="6" fill="#2A2A38"/>
|
||||
<text x="12" y="21" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="500" fill="#8E8E93">📅 Calendar</text>
|
||||
</g>
|
||||
<g transform="translate(220, 0)">
|
||||
<rect x="0" y="0" width="110" height="32" rx="6" fill="#2A2A38"/>
|
||||
<text x="12" y="21" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="500" fill="#8E8E93">🛒 Shopping</text>
|
||||
</g>
|
||||
<g transform="translate(340, 0)">
|
||||
<rect x="0" y="0" width="90" height="32" rx="6" fill="#2A2A38"/>
|
||||
<text x="12" y="21" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="500" fill="#8E8E93">🍽 Meals</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
// Position screenshot on the right side
|
||||
const screenshotX = width - screenshotWidth - 40;
|
||||
const screenshotY = Math.floor((height - screenshotHeight) / 2);
|
||||
|
||||
// Composite everything together
|
||||
const image = await sharp({
|
||||
create: {
|
||||
width,
|
||||
height,
|
||||
channels: 4,
|
||||
background: BG_COLOR
|
||||
}
|
||||
})
|
||||
.composite([
|
||||
{
|
||||
input: Buffer.from(svg),
|
||||
top: 0,
|
||||
left: 0
|
||||
},
|
||||
{
|
||||
input: screenshot,
|
||||
top: screenshotY,
|
||||
left: screenshotX
|
||||
}
|
||||
])
|
||||
.png()
|
||||
.toFile(`docs/${filename}`);
|
||||
|
||||
console.log(`✓ Created docs/${filename} (${width}x${height})`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('Generating social preview images...\n');
|
||||
|
||||
for (const [filename, { width, height }] of Object.entries(DIMENSIONS)) {
|
||||
await createSocialImage(filename, width, height);
|
||||
}
|
||||
|
||||
console.log('\n✓ All social images generated successfully!');
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('Error:', err.message);
|
||||
process.exit(1);
|
||||
});
|
||||