From d710d552c7e65e15a1031d0bfe0acabafbbf7040 Mon Sep 17 00:00:00 2001
From: Nicholas Petrasek
Date: Thu, 1 Oct 2026 10:07:36 +0200
Subject: [PATCH] feat: add homepage project support links
---
test/browser/website.spec.ts | 70 +++++++++++++++++++++++++++++++++++-
website/index.html | 21 +++++++++++
website/styles.css | 26 ++++++++++++++
3 files changed, 116 insertions(+), 1 deletion(-)
diff --git a/test/browser/website.spec.ts b/test/browser/website.spec.ts
index d043d0e..137d7af 100644
--- a/test/browser/website.spec.ts
+++ b/test/browser/website.spec.ts
@@ -1,5 +1,64 @@
import { expect, test } from '@playwright/test';
+for (const width of [1280, 390, 320]) {
+ test(`homepage support links remain usable at ${width}px`, async ({
+ page,
+ browserName,
+ }) => {
+ await page.setViewportSize({ width, height: 900 });
+ await page.goto(websiteOrigin + '/');
+ const support = page.getByRole('region', {
+ name: 'Useful in your project?',
+ });
+ await expect(support).toBeVisible();
+ expect(
+ await support.evaluate((node) => node.previousElementSibling?.id),
+ ).toBe('use-cases');
+ const star = support.getByRole('link', {
+ name: 'Star on GitHub',
+ exact: true,
+ });
+ const explore = support.getByRole('link', {
+ name: 'Explore NIPE Open Source',
+ exact: true,
+ });
+ await expect(star).toHaveAttribute(
+ 'href',
+ 'https://github.com/NIPE-Solutions/readonly-view',
+ );
+ await expect(explore).toHaveAttribute(
+ 'href',
+ 'https://opensource.nipesolutions.com',
+ );
+ for (const link of [star, explore]) {
+ const bounds = await link.boundingBox();
+ expect(bounds!.height).toBeGreaterThanOrEqual(44);
+ expect(bounds!.width).toBeGreaterThanOrEqual(44);
+ expect(bounds!.x).toBeGreaterThanOrEqual(0);
+ expect(bounds!.x + bounds!.width).toBeLessThanOrEqual(width);
+ expect(await link.evaluate((node) => node.tagName)).toBe('A');
+ }
+ await star.focus();
+ // macOS Safari navigates native links with Option+Tab.
+ const tab =
+ browserName === 'webkit' && process.platform === 'darwin'
+ ? 'Alt+Tab'
+ : 'Tab';
+ await page.keyboard.press(tab);
+ await expect(explore).toBeFocused();
+ await page.keyboard.press(`Shift+${tab}`);
+ await expect(star).toBeFocused();
+ expect(
+ await star.evaluate((node) => getComputedStyle(node).outlineStyle),
+ ).not.toBe('none');
+ if (process.env.CTA_SCREENSHOT_DIR) {
+ await support.screenshot({
+ path: `${process.env.CTA_SCREENSHOT_DIR}/${test.info().project.name}-${width}.png`,
+ });
+ }
+ });
+}
+
const websiteOrigin = 'http://127.0.0.1:42873';
test('documentation navigation and live demo work', async ({ page }) => {
@@ -86,6 +145,7 @@ test('documentation has no narrow-screen overflow', async ({ page }) => {
test('sticky documentation navigation keeps keyboard focus visible in a short viewport', async ({
page,
+ browserName,
}) => {
await page.setViewportSize({ width: 800, height: 600 });
await page.emulateMedia({ reducedMotion: 'reduce' });
@@ -98,10 +158,15 @@ test('sticky documentation navigation keeps keyboard focus visible in a short vi
const firstLink = links.first();
const lastLink = links.last();
const linkCount = await links.count();
+ // macOS Safari navigates native links with Option+Tab.
+ const tab =
+ browserName === 'webkit' && process.platform === 'darwin'
+ ? 'Alt+Tab'
+ : 'Tab';
await firstLink.focus();
for (let index = 1; index < linkCount; index += 1) {
- await page.keyboard.press('Tab');
+ await page.keyboard.press(tab);
}
await expect(lastLink).toBeFocused();
@@ -140,6 +205,9 @@ for (const [route, heading, language] of legalRoutes) {
});
await page.setViewportSize({ width, height: 844 });
await page.goto(websiteOrigin + route);
+ await expect(
+ page.getByRole('region', { name: 'Useful in your project?' }),
+ ).toHaveCount(0);
await expect(
page.getByRole('heading', { level: 1, name: heading }),
).toBeVisible();
diff --git a/website/index.html b/website/index.html
index 67bfe0d..feb825e 100644
--- a/website/index.html
+++ b/website/index.html
@@ -234,6 +234,27 @@ Plugin context
>.
+
+ Useful in your project?
+
+ If ReadonlyView helps you share live data safely, a
+ GitHub star helps other developers discover it.
+ Explore more tools from NIPE Open Source for your
+ next project.
+
+
+
Choose the right tool
diff --git a/website/styles.css b/website/styles.css
index aec7e3f..af67833 100644
--- a/website/styles.css
+++ b/website/styles.css
@@ -221,6 +221,32 @@ code {
color: var(--blue);
border-bottom-color: var(--blue);
}
+.support-actions {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 12px 20px;
+ margin-top: 28px;
+}
+.support-actions a {
+ display: inline-flex;
+ align-items: center;
+ min-height: 44px;
+ padding: 12px 16px;
+ border: 1px solid var(--rule);
+ border-radius: 6px;
+ font-size: 14px;
+ font-weight: 650;
+}
+.support-actions a:first-child {
+ background: var(--blue);
+ border-color: var(--blue);
+ color: white;
+}
+.support-actions a:hover {
+ text-decoration: underline;
+ text-underline-offset: 3px;
+}
.docs-layout {
max-width: 1440px;
margin: auto;