From 917d7b3d5a7e37377ee8fb3a0d17ebf071595c53 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 29 Apr 2024 00:28:17 +0100 Subject: [PATCH] pixel perfect scaling even on high density screens --- website/agb/src/app/layout.tsx | 6 +-- website/agb/src/app/page.tsx | 2 + website/agb/src/app/registry.tsx | 19 +++++++++- website/agb/src/app/showcase/[game]/page.tsx | 16 +------- .../agb/src/app/showcase/[game]/styled.tsx | 38 ++++++++++++++----- website/agb/src/app/showcase/styles.tsx | 9 +++-- website/agb/src/components/contentBlock.tsx | 7 +--- 7 files changed, 60 insertions(+), 37 deletions(-) diff --git a/website/agb/src/app/layout.tsx b/website/agb/src/app/layout.tsx index 60d9b5d5..3bb3db51 100644 --- a/website/agb/src/app/layout.tsx +++ b/website/agb/src/app/layout.tsx @@ -1,6 +1,6 @@ import type { Metadata } from "next"; import "./globalStyles.css"; -import StyledComponentsRegistry from "./registry"; +import StyledComponentsRegistry, { BodyPixelRatio } from "./registry"; export const metadata: Metadata = { title: "agb - a rust framework for making Game Boy Advance games", @@ -13,9 +13,9 @@ export default function RootLayout({ }>) { return ( - + {children} - + ); } diff --git a/website/agb/src/app/page.tsx b/website/agb/src/app/page.tsx index 225ede6d..e24dc409 100644 --- a/website/agb/src/app/page.tsx +++ b/website/agb/src/app/page.tsx @@ -16,6 +16,8 @@ import { ExternalLink } from "@/components/externalLink"; const HelpLinks = styled.div` display: flex; + flex-wrap: wrap; + gap: 16px; justify-content: space-around; `; diff --git a/website/agb/src/app/registry.tsx b/website/agb/src/app/registry.tsx index fa12293e..45552c80 100644 --- a/website/agb/src/app/registry.tsx +++ b/website/agb/src/app/registry.tsx @@ -1,8 +1,8 @@ "use client"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { useServerInsertedHTML } from "next/navigation"; -import { ServerStyleSheet, StyleSheetManager } from "styled-components"; +import styled, { ServerStyleSheet, StyleSheetManager } from "styled-components"; export default function StyledComponentsRegistry({ children, @@ -27,3 +27,18 @@ export default function StyledComponentsRegistry({ ); } + +const BodyWithPixelRatio = styled.body<{ + $pixel?: number; +}>` + --device-pixel: calc(1px / ${(props) => props.$pixel}); +`; + +export function BodyPixelRatio({ children }: { children: React.ReactNode }) { + const [pixel, setPixel] = useState(1); + useEffect(() => { + setPixel(window.devicePixelRatio); + }, []); + + return {children}; +} diff --git a/website/agb/src/app/showcase/[game]/page.tsx b/website/agb/src/app/showcase/[game]/page.tsx index e4b82f72..4e943473 100644 --- a/website/agb/src/app/showcase/[game]/page.tsx +++ b/website/agb/src/app/showcase/[game]/page.tsx @@ -3,13 +3,11 @@ import { Games, ShowcaseGame } from "../games"; import { ContentBlock } from "@/components/contentBlock"; import { ExternalLink } from "@/components/externalLink"; import Link from "next/link"; -import Image, { StaticImageData } from "next/image"; import { - ScreenshotsWrapper, - ScreenshotWrapper, BackToShowcaseWrapper, DescriptionAndScreenshots, Description, + Screenshots, } from "./styled"; export async function generateStaticParams() { @@ -51,18 +49,6 @@ function DeveloperNames({ names }: { names: string[] }) { return first.join(", ") + `, and ${names[names.length - 1]}`; } -function Screenshots({ screenshots }: { screenshots: StaticImageData[] }) { - return ( - - {screenshots.map((screenshot) => ( - - - - ))} - - ); -} - function Display({ game }: { game: ShowcaseGame }) { return ( <> diff --git a/website/agb/src/app/showcase/[game]/styled.tsx b/website/agb/src/app/showcase/[game]/styled.tsx index 8ee4f0ce..2b8e51f4 100644 --- a/website/agb/src/app/showcase/[game]/styled.tsx +++ b/website/agb/src/app/showcase/[game]/styled.tsx @@ -1,19 +1,35 @@ "use client"; import { styled } from "styled-components"; +import Image, { StaticImageData } from "next/image"; -export const ScreenshotsWrapper = styled.div` +export function Screenshots({ + screenshots, +}: { + screenshots: StaticImageData[]; +}) { + return ( + + {screenshots.map((screenshot) => ( + + ))} + + ); +} + +const ScreenshotsWrapper = styled.div` flex: 4; + text-align: center; `; -export const ScreenshotWrapper = styled.div` - text-align: center; - img { - width: 100%; - width: round(down, 100%, 240px); - height: auto; - image-rendering: pixelated; - } +const Screenshot = styled(Image)` + width: 100%; + width: max( + round(down, 100%, calc(240 * var(--device-pixel))), + calc(240 * var(--device-pixel)) + ); + height: auto; + image-rendering: pixelated; `; export const Description = styled.div` @@ -26,6 +42,10 @@ export const Description = styled.div` export const DescriptionAndScreenshots = styled.div` display: flex; gap: 16px; + + @media (max-width: 1000px) { + display: block; + } `; export const BackToShowcaseWrapper = styled.div` diff --git a/website/agb/src/app/showcase/styles.tsx b/website/agb/src/app/showcase/styles.tsx index 33c6ac08..6eac2dae 100644 --- a/website/agb/src/app/showcase/styles.tsx +++ b/website/agb/src/app/showcase/styles.tsx @@ -6,20 +6,23 @@ import Image from "next/image"; export const GameGrid = styled.div` display: grid; - grid-template-columns: repeat(auto-fit, 600px); + grid-template-columns: repeat(auto-fit, min(100vw, 600px)); justify-content: center; gap: 48px; `; export const GameImage = styled(Image)` width: 100%; - width: round(down, 100%, 240px); + width: max( + round(down, 100%, calc(240 * var(--device-pixel))), + min(calc(240 * var(--device-pixel)), 100vw) + ); height: auto; image-rendering: pixelated; `; export const GameDisplay = styled(Link)` - width: 600px; + width: 100%; text-align: center; color: black; text-decoration: none; diff --git a/website/agb/src/components/contentBlock.tsx b/website/agb/src/components/contentBlock.tsx index 8a05d749..20e7e3a1 100644 --- a/website/agb/src/components/contentBlock.tsx +++ b/website/agb/src/components/contentBlock.tsx @@ -14,11 +14,8 @@ const Section = styled.section<{ $color: string }>` const CENTERED_CSS = ` margin-left: auto; margin-right: auto; - max-width: 60%; - - @media (max-width: 40rem) { - max-width: 90%; - } + width: 60%; + min-width: min(95%, 1000px); `; export const CenteredBlock = styled.div`