web: logo on the login page, icon as favicon and in the headers
ci / images (push) Canceled after 0s
ci / check (push) Canceled after 3s

public/logo.png is the source; icon-192.png (the mark, cropped and padded
on the logo's background) and logo-512.png (login card) are cut from it
with ffmpeg so pages don't load the 1.2 MB original. Brand (icon + name)
replaces the plain app name in the admin and My Phone headers.
This commit is contained in:
iamdoubz committed 2026-10-19 04:34:08 -05:00
1 parent 863a9d121c
commit 964a3c0b25
8 files changed
+25 -8

No files matched your search

+2
View File
@@ -4,6 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SelfPhoneNG</title>
<link rel="icon" type="image/png" href="/icon-192.png" />
<link rel="apple-touch-icon" href="/icon-192.png" />
<script>
// Apply the theme before first paint so dark mode doesn't flash.
try {
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

+11
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// Small form building blocks shared by every page.
import { useId, type ComponentProps, type ReactNode } from 'react';
import { APP_NAME } from '@selfphoneng/shared';
const focus =
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500';
@@ -13,6 +14,16 @@ const variants = {
ghost: 'hover:bg-zinc-100 dark:hover:bg-zinc-800',
};
/** The icon (public/icon-192.png, cut from public/logo.png) and the product name. */
export function Brand({ className = '' }: { className?: string }) {
return (
<span className={`inline-flex items-center gap-2 font-semibold ${className}`}>
<img src="/icon-192.png" alt="" className="size-7 rounded-md" />
{APP_NAME}
</span>
);
}
export function Button({
variant = 'secondary',
className = '',
+1 -1
View File
@@ -13,7 +13,7 @@ export function AuthCard({ title, children }: { title: string; children: React.R
return (
<main className="flex min-h-dvh items-center justify-center p-4">
<div className="w-full max-w-sm rounded-lg border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
<p className="mb-1 text-sm font-semibold text-sky-600">{APP_NAME}</p>
<img src="/logo-512.png" alt={APP_NAME} className="mx-auto mb-4 size-32 rounded-xl" />
<h1 className="mb-5 text-xl font-semibold">{title}</h1>
{children}
</div>
+6 -4
View File
@@ -3,10 +3,10 @@
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useEffect, useState } from 'react';
import { Navigate, NavLink, Outlet, useNavigate } from 'react-router';
import { APP_NAME, type AuthUser } from '@selfphoneng/shared';
import type { AuthUser } from '@selfphoneng/shared';
import { authApi, systemApi } from '../../api/client.ts';
import { ThemeToggle } from '../../components/ThemeToggle.tsx';
import { Button } from '../../components/ui.tsx';
import { Brand, Button } from '../../components/ui.tsx';
import { subscribeLive, useLive } from '../../live/store.ts';
export const ADMIN_PAGES = [
@@ -75,7 +75,9 @@ export function AdminLayout({ user }: { user: AuthUser }) {
return (
<div className="flex min-h-dvh">
<aside className="sticky top-0 hidden h-dvh w-60 flex-col border-r border-zinc-200 bg-zinc-50 md:flex dark:border-zinc-800 dark:bg-zinc-900">
<p className="px-4 py-4 text-lg font-semibold">{APP_NAME}</p>
<p className="px-4 py-4 text-lg">
<Brand />
</p>
{links}
</aside>
@@ -91,7 +93,7 @@ export function AdminLayout({ user }: { user: AuthUser }) {
>
☰
</Button>
<span className="font-semibold md:hidden">{APP_NAME}</span>
<Brand className="md:hidden" />
<div className="ml-auto flex items-center gap-1 whitespace-nowrap sm:gap-2">
<span className="hidden text-sm text-zinc-600 sm:inline dark:text-zinc-400">
{user.displayName}
+5 -3
View File
@@ -4,10 +4,10 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useEffect } from 'react';
import { Link, NavLink, Route, Routes, useNavigate } from 'react-router';
import { APP_NAME, type AuthUser } from '@selfphoneng/shared';
import type { AuthUser } from '@selfphoneng/shared';
import { authApi, meApi } from '../../api/client.ts';
import { ThemeToggle } from '../../components/ThemeToggle.tsx';
import { Alert, Button } from '../../components/ui.tsx';
import { Alert, Brand, Button } from '../../components/ui.tsx';
import { subscribeLive, useLive } from '../../live/store.ts';
import { SoftphoneDock } from '../../softphone/SoftphoneDock.tsx';
import { PresencePanel } from '../../softphone/PresencePanel.tsx';
@@ -40,7 +40,9 @@ export function MePage({ user }: { user: AuthUser }) {
return (
<main className="mx-auto flex min-h-dvh max-w-6xl flex-col gap-6 p-4 sm:p-8">
<header className="flex flex-wrap items-center justify-between gap-2">
<h1 className="text-xl font-semibold">{APP_NAME}</h1>
<h1 className="text-xl">
<Brand />
</h1>
<div className="flex items-center gap-2">
{user.role === 'admin' && (
<Link className={`${link} text-sm`} to="/admin">