Bye bye .png

... well mostly. Some places will still use it like PWA splash screens.
Otherwise browser support for .avif strong enough now to do this
now.
This commit is contained in:
Kalle
2026-03-29 16:48:47 +03:00
parent 4867858cff
commit e27260f88b
2389 changed files with 41 additions and 105 deletions

View File

@@ -18,7 +18,7 @@ STORAGE_REGION=us-east-1
STORAGE_BUCKET=sendou
STORAGE_URL=http://127.0.0.1:9000
VITE_TOURNAMENT_DEFAULT_LOGO="tournament-logo-default.png"
VITE_TOURNAMENT_DEFAULT_LOGO="tournament-logo-default.avif"
// Twitch integration for fetching streams
TWITCH_CLIENT_ID=

View File

@@ -7,4 +7,4 @@ BASE_URL=https://example.com
SKALOP_SYSTEM_MESSAGE_URL=http://skalop.test
SKALOP_TOKEN=test
VITE_TOURNAMENT_DEFAULT_LOGO="tournament-logo-test.png"
VITE_TOURNAMENT_DEFAULT_LOGO="tournament-logo-test.avif"

View File

@@ -35,7 +35,6 @@ interface ImageProps {
testId?: string;
onClick?: () => void;
loading?: "lazy";
forcePng?: boolean;
}
export function Image({
@@ -52,50 +51,18 @@ export function Image({
containerStyle,
onClick,
loading,
forcePng,
}: ImageProps) {
if (forcePng) {
return (
// biome-ignore lint/a11y/noStaticElementInteractions: Biome v2 migration
<div
title={title}
className={containerClassName}
style={containerStyle}
onClick={onClick}
>
<img
alt={alt}
src={`${path}.png`}
className={className}
width={size ?? width}
height={size ?? height}
style={style}
draggable="false"
loading={loading}
data-testid={testId}
/>
</div>
);
}
return (
// biome-ignore lint/a11y/noStaticElementInteractions: Biome v2 migration
<picture
<div
title={title}
className={containerClassName}
style={containerStyle}
onClick={onClick}
>
<source
type="image/avif"
srcSet={`${path}.avif`}
width={width}
height={height}
style={style}
/>
<img
alt={alt}
src={`${path}.png`}
src={`${path}.avif`}
className={className}
width={size ?? width}
height={size ?? height}
@@ -104,7 +71,7 @@ export function Image({
loading={loading}
data-testid={testId}
/>
</picture>
</div>
);
}

View File

@@ -2729,7 +2729,7 @@ async function notifications() {
{
type: "TO_CHECK_IN_OPENED",
meta: { tournamentId: 1, tournamentName: "PICNIC #2" },
pictureUrl: "/static-assets/img/tournament-logos/pn.png",
pictureUrl: "/static-assets/img/tournament-logos/pn.avif",
},
];

View File

@@ -134,7 +134,7 @@ function EventSchedule({
<div className="stack md horizontal items-center mx-auto">
<img
alt=""
src={`http://localhost:5173/static-assets/img/tournament-logos/${imgId}.png`}
src={`http://localhost:5173/static-assets/img/tournament-logos/${imgId}.avif`}
style={{
width: "4rem",
height: "4rem",

View File

@@ -117,7 +117,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
name: badge.displayName,
count: badge.count,
gifUrl: `https://sendou.ink/static-assets/badges/${badge.code}.gif`,
imageUrl: `https://sendou.ink/static-assets/badges/${badge.code}.png`,
imageUrl: `https://sendou.ink/static-assets/badges/${badge.code}.avif`,
})),
teams: user.teams.map((team) => ({
id: team.id,

View File

@@ -152,7 +152,7 @@ export interface GetTournamentResponse {
*/
url: string;
/**
* @example "https://sendou.ink/static-assets/img/tournament-logos/itz.png"
* @example "https://sendou.ink/static-assets/img/tournament-logos/itz.avif"
*/
logoUrl: string | null;
/**
@@ -458,7 +458,7 @@ type Badge = {
name: string;
count: number;
/**
* @example "https://sendou.ink/static-assets/badges/monday.png"
* @example "https://sendou.ink/static-assets/badges/monday.avif"
*/
imageUrl: string;
/**

View File

@@ -434,7 +434,7 @@ function WeaponImageSelector() {
<DraggableWeaponButton
key={weaponId}
id={`main-${weaponId}`}
src={`${outlinedMainWeaponImageUrl(weaponId)}.png`}
src={`${outlinedMainWeaponImageUrl(weaponId)}.avif`}
imgPath={mainWeaponImageUrl(weaponId)}
previewPath={outlinedMainWeaponImageUrl(weaponId)}
alt={t(`weapons:MAIN_${weaponId}`)}
@@ -458,7 +458,7 @@ function WeaponImageSelector() {
<DraggableWeaponButton
key={subWeaponId}
id={`sub-${subWeaponId}`}
src={`${subWeaponImageUrl(subWeaponId)}.png`}
src={`${subWeaponImageUrl(subWeaponId)}.avif`}
imgPath={subWeaponImageUrl(subWeaponId)}
previewPath={subWeaponImageUrl(subWeaponId)}
alt={t(`weapons:SUB_${subWeaponId}`)}
@@ -485,7 +485,7 @@ function WeaponImageSelector() {
<DraggableWeaponButton
key={specialWeaponId}
id={`special-${specialWeaponId}`}
src={`${specialWeaponImageUrl(specialWeaponId)}.png`}
src={`${specialWeaponImageUrl(specialWeaponId)}.avif`}
imgPath={specialWeaponImageUrl(specialWeaponId)}
previewPath={specialWeaponImageUrl(specialWeaponId)}
alt={t(`weapons:SPECIAL_${specialWeaponId}`)}
@@ -507,7 +507,7 @@ function WeaponImageSelector() {
<DraggableWeaponButton
key={mode}
id={`mode-${mode}`}
src={`${modeImageUrl(mode)}.png`}
src={`${modeImageUrl(mode)}.avif`}
imgPath={modeImageUrl(mode)}
previewPath={modeImageUrl(mode)}
alt={t(`game-misc:MODE_LONG_${mode}`)}

View File

@@ -151,7 +151,7 @@ function MapButton({
[styles.mapButtonWiggle]: wiggle,
[styles.mapButtonGreyedOut]: selected || banned || tiebreaker,
})}
style={{ "--map-image-url": `url("${stageImageUrl(stageId)}.png")` }}
style={{ "--map-image-url": `url("${stageImageUrl(stageId)}.avif")` }}
onClick={onClick}
disabled={banned}
type="button"

View File

@@ -136,10 +136,10 @@ export async function getSendouQSidebarStreams(): Promise<
id: `sendouq-${matchId}`,
name: `Match #${matchId}`,
imageUrl: averageTier
? `${tierImageUrl(averageTier.name)}.png`
: `${navIconUrl("sendouq")}.png`,
? `${tierImageUrl(averageTier.name)}.avif`
: `${navIconUrl("sendouq")}.avif`,
overlayIconUrl: averageTier
? `${navIconUrl("sendouq")}.png`
? `${navIconUrl("sendouq")}.avif`
: undefined,
url: SENDOUQ_STREAMS_PAGE,
subtitle: averageTier

View File

@@ -217,9 +217,9 @@ describe("GroupCard", () => {
}),
});
// Weapons are rendered as picture elements
const pictures = screen.container.querySelectorAll("picture");
expect(pictures.length).toBeGreaterThan(0);
// Weapons are rendered as img elements
const images = screen.container.querySelectorAll("img");
expect(images.length).toBeGreaterThan(0);
});
});

View File

@@ -375,7 +375,7 @@ export function tournamentToSidebarEvent(
};
}
const SCRIMS_ICON_URL = `${navIconUrl("scrims")}.png`;
const SCRIMS_ICON_URL = `${navIconUrl("scrims")}.avif`;
export function scrimToSidebarEvent(s: SidebarScrim): SidebarEvent {
return {

View File

@@ -7,10 +7,9 @@ import { TierListItemImage } from "./TierListItemImage";
interface DraggableItemProps {
item: TierListItem;
forcePng?: boolean;
}
export function DraggableItem({ item, forcePng }: DraggableItemProps) {
export function DraggableItem({ item }: DraggableItemProps) {
const uniqueId = tierListItemId(item);
const {
@@ -33,7 +32,7 @@ export function DraggableItem({ item, forcePng }: DraggableItemProps) {
return (
<div ref={setNodeRef} className={styles.item} style={style}>
<div data-item-id={uniqueId} {...listeners} {...attributes}>
<TierListItemImage item={item} forcePng={forcePng} />
<TierListItemImage item={item} />
</div>
</div>
);

View File

@@ -11,58 +11,38 @@ import styles from "./TierListItemImage.module.css";
interface TierListItemImageProps {
item: TierListItem;
forcePng?: boolean;
}
export function TierListItemImage({ item, forcePng }: TierListItemImageProps) {
export function TierListItemImage({ item }: TierListItemImageProps) {
switch (item.type) {
case "main-weapon":
return (
<div className={styles.imageWrapper}>
<WeaponImage
weaponSplId={item.id}
variant="badge"
size={48}
forcePng={forcePng}
/>
<WeaponImage weaponSplId={item.id} variant="badge" size={48} />
</div>
);
case "sub-weapon":
return (
<div className={styles.imageWrapper}>
<SubWeaponImage subWeaponId={item.id} size={48} forcePng={forcePng} />
<SubWeaponImage subWeaponId={item.id} size={48} />
</div>
);
case "special-weapon":
return (
<div className={styles.imageWrapper}>
<SpecialWeaponImage
specialWeaponId={item.id}
size={48}
forcePng={forcePng}
/>
<SpecialWeaponImage specialWeaponId={item.id} size={48} />
</div>
);
case "stage":
return (
<div className={styles.imageWrapper}>
<StageImage
stageId={item.id}
width={80}
className="rounded-sm"
forcePng={forcePng}
/>
<StageImage stageId={item.id} width={80} className="rounded-sm" />
</div>
);
case "mode":
return (
<div className={styles.imageWrapper}>
<ModeImage
mode={item.id}
width={48}
height={48}
forcePng={forcePng}
/>
<ModeImage mode={item.id} width={48} height={48} />
</div>
);
case "stage-mode": {
@@ -71,18 +51,12 @@ export function TierListItemImage({ item, forcePng }: TierListItemImageProps) {
return (
<div className={styles.imageWrapper}>
<div className="relative">
<StageImage
stageId={stageId}
width={80}
className="rounded-sm"
forcePng={forcePng}
/>
<StageImage stageId={stageId} width={80} className="rounded-sm" />
<ModeImage
mode={mode as ModeShort}
width={24}
height={24}
className={styles.modeOverlay}
forcePng={forcePng}
/>
</div>
</div>

View File

@@ -163,11 +163,7 @@ export function TierRow({ tier }: TierRowProps) {
strategy={horizontalListSortingStrategy}
>
{items.map((item) => (
<DraggableItem
key={tierListItemId(item)}
item={item}
forcePng={screenshotMode}
/>
<DraggableItem key={tierListItemId(item)} item={item} />
))}
</SortableContext>
) : null}

View File

@@ -208,7 +208,7 @@ function MapButton({
className={clsx(styles.mapButton, {
[styles.mapButtonGreyedOut]: selected || disabled,
})}
style={{ "--map-image-url": `url("${stageImageUrl(stageId)}.png")` }}
style={{ "--map-image-url": `url("${stageImageUrl(stageId)}.avif")` }}
onClick={onClick}
type="button"
disabled={!onClick}

View File

@@ -288,7 +288,7 @@ function FancyStageBanner({
const gamesCompleted = data.results.length;
const stageNameToBannerImageUrl = (stageId: StageId) => {
return `${stageImageUrl(stageId)}.png`;
return `${stageImageUrl(stageId)}.avif`;
};
const banPickingTeam = () => {

View File

@@ -2192,7 +2192,7 @@ export const SWIM_OR_SINK_167 = (
},
],
},
logoUrl: "test.png",
logoUrl: "test.avif",
author: {
id: 1402,
username: "grace",

View File

@@ -1459,7 +1459,7 @@ export const PADDLING_POOL_257 = () =>
description:
"Hosted by Dapple Productions.\n\nThe longest tournament series in Splatoon!\nEvery week a tournament!\n\n✓ DE or Groups into SE\n✓ All Modes (Picnic system)\n✓ Badge prize\n✓ A well-ran tournament experience\n\nCome join!",
rules: null,
logoUrl: "/test.png",
logoUrl: "/test.avif",
startTime: 1709748000,
author: {
id: 860,
@@ -8214,7 +8214,7 @@ export const PADDLING_POOL_255 = () =>
name: "Paddling Pool 255",
description: null,
rules: null,
logoUrl: "/test.png",
logoUrl: "/test.avif",
startTime: 1708538400,
author: {
id: 860,
@@ -15300,7 +15300,7 @@ export const IN_THE_ZONE_32 = ({
name: "In The Zone 32",
description: "Part of sendou.ink ranked season 2",
rules: null,
logoUrl: "/test.png",
logoUrl: "/test.avif",
startTime: 1707588000,
author: {
id: 274,

View File

@@ -67,7 +67,7 @@ export const testTournament = ({
tentativeTier: null,
parentTournamentId: null,
rules: null,
logoUrl: "/test.png",
logoUrl: "/test.avif",
discordUrl: null,
startTime: 1705858842,
isFinalized: 0,

View File

@@ -14,7 +14,7 @@ Check out `sendou.ink/app/features/api-public/schema.ts`
```bash
sendou@macbook ~ % curl -H "Authorization: Bearer mytoken" https://sendou.ink/api/tournament/1
{"name":"PICNIC mini","startTime":"2023-05-18T18:00:00.000Z","url":"https://sendou.ink/to/1/brackets","logoUrl":"https://sendou.ink/static-assets/img/tournament-logos/pn.png","teams":{"checkedInCount":25,"registeredCount":31},"brackets":[{"name":"Main bracket","type":"double_elimination"}],"organizationId":1,"isFinalized":true}%
{"name":"PICNIC mini","startTime":"2023-05-18T18:00:00.000Z","url":"https://sendou.ink/to/1/brackets","logoUrl":"https://sendou.ink/static-assets/img/tournament-logos/pn.avif","teams":{"checkedInCount":25,"registeredCount":31},"brackets":[{"name":"Main bracket","type":"double_elimination"}],"organizationId":1,"isFinalized":true}%
```
## Clients (unofficial)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 803 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 815 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Some files were not shown because too many files have changed in this diff Show More