From 997ec5e6c1fb958da89b46103aa42d33f053fccc Mon Sep 17 00:00:00 2001 From: Iu Date: Sat, 10 Oct 2026 13:19:29 +0300 Subject: [PATCH] feat(frontend): replace emoji with inline Material Symbols icon system Add a self-contained icon system: vendored Material Symbols registry, an component that references a single sprite via , plus IconButton/IconSprite. Replace every emoji/Unicode glyph (logo, theme switcher, cover placeholder, like/comment counts, lightbox controls, back links) and add leading icons to key controls. Decorative icons are aria-hidden; icon-only buttons get labels; theme toggles expose aria-pressed; counters get accessible names. Recorded in openspec change material-icons-ui (all tasks complete). --- frontend/src/lib/components/Comments.svelte | 18 +++- frontend/src/lib/components/Icon.svelte | 27 ++++++ frontend/src/lib/components/IconButton.svelte | 40 +++++++++ frontend/src/lib/components/IconSprite.svelte | 13 +++ frontend/src/lib/components/Lightbox.svelte | 35 ++++++-- frontend/src/lib/components/PhotoGrid.svelte | 18 +++- .../src/lib/components/ThemeSwitcher.svelte | 36 ++++---- frontend/src/lib/components/Uploader.svelte | 5 +- frontend/src/lib/icons/registry.ts | 46 ++++++++++ frontend/src/routes/+layout.svelte | 23 ++++- frontend/src/routes/+page.svelte | 26 ++++-- frontend/src/routes/albums/[id]/+page.svelte | 20 +++-- .../routes/albums/[id]/members/+page.svelte | 42 +++++++-- .../routes/albums/[id]/settings/+page.svelte | 39 +++++++-- .../src/routes/invites/[token]/+page.svelte | 12 ++- .../src/routes/share/[token]/+page.svelte | 40 +++++++-- .../changes/material-icons-ui/.openspec.yaml | 2 + openspec/changes/material-icons-ui/design.md | 85 +++++++++++++++++++ .../changes/material-icons-ui/proposal.md | 46 ++++++++++ .../specs/ui-iconography/spec.md | 43 ++++++++++ openspec/changes/material-icons-ui/tasks.md | 35 ++++++++ 21 files changed, 582 insertions(+), 69 deletions(-) create mode 100644 frontend/src/lib/components/Icon.svelte create mode 100644 frontend/src/lib/components/IconButton.svelte create mode 100644 frontend/src/lib/components/IconSprite.svelte create mode 100644 frontend/src/lib/icons/registry.ts create mode 100644 openspec/changes/material-icons-ui/.openspec.yaml create mode 100644 openspec/changes/material-icons-ui/design.md create mode 100644 openspec/changes/material-icons-ui/proposal.md create mode 100644 openspec/changes/material-icons-ui/specs/ui-iconography/spec.md create mode 100644 openspec/changes/material-icons-ui/tasks.md diff --git a/frontend/src/lib/components/Comments.svelte b/frontend/src/lib/components/Comments.svelte index 7832fdc..93a0acd 100644 --- a/frontend/src/lib/components/Comments.svelte +++ b/frontend/src/lib/components/Comments.svelte @@ -1,5 +1,6 @@ + +{#if known} + + + +{/if} diff --git a/frontend/src/lib/components/IconButton.svelte b/frontend/src/lib/components/IconButton.svelte new file mode 100644 index 0000000..7337458 --- /dev/null +++ b/frontend/src/lib/components/IconButton.svelte @@ -0,0 +1,40 @@ + + + diff --git a/frontend/src/lib/components/IconSprite.svelte b/frontend/src/lib/components/IconSprite.svelte new file mode 100644 index 0000000..bcaf629 --- /dev/null +++ b/frontend/src/lib/components/IconSprite.svelte @@ -0,0 +1,13 @@ + + + + diff --git a/frontend/src/lib/components/Lightbox.svelte b/frontend/src/lib/components/Lightbox.svelte index c75a602..5af7be3 100644 --- a/frontend/src/lib/components/Lightbox.svelte +++ b/frontend/src/lib/components/Lightbox.svelte @@ -2,6 +2,7 @@ import { api } from '$lib/api'; import type { Photo } from '$lib/types'; import Comments from './Comments.svelte'; + import Icon from './Icon.svelte'; interface Props { photos: Photo[]; @@ -55,9 +56,11 @@ class="bg-bg/95 fixed inset-0 z-50 flex flex-col" role="dialog" aria-modal="true" + tabindex="-1" onclick={(e) => { if (e.target === e.currentTarget) onClose(); }} + onkeydown={onKey} ontouchstart={onTouchStart} ontouchend={onTouchEnd} > @@ -67,23 +70,37 @@ {#if fresh?.download_url} Скачать + + Скачать + {/if} + +
- + + {#if current} › + +
{#if current} diff --git a/frontend/src/lib/components/PhotoGrid.svelte b/frontend/src/lib/components/PhotoGrid.svelte index a023df4..b7bbcb6 100644 --- a/frontend/src/lib/components/PhotoGrid.svelte +++ b/frontend/src/lib/components/PhotoGrid.svelte @@ -1,4 +1,5 @@ -
+
{#each options as option (option.value)} - + /> {/each}
diff --git a/frontend/src/lib/components/Uploader.svelte b/frontend/src/lib/components/Uploader.svelte index 4a46907..69bd165 100644 --- a/frontend/src/lib/components/Uploader.svelte +++ b/frontend/src/lib/components/Uploader.svelte @@ -1,5 +1,6 @@ +
- 📷 Galo + + + Galo +
{#if auth.isAuthenticated} + + Выйти + {:else} - Войти + + + Войти + {/if}
diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 6906b20..0ce6b47 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -1,5 +1,6 @@ {#if error}

{error}

{/if} @@ -105,9 +117,12 @@ removeMember(member.user_id)} > + + Удалить + {:else} {member.role} {/if} @@ -148,11 +163,26 @@ />
- + + Создать ссылку + {#if inviteUrl} -

Ссылка: {inviteUrl}

+
+ Ссылка: {inviteUrl} + +
{/if} {/if} diff --git a/frontend/src/routes/albums/[id]/settings/+page.svelte b/frontend/src/routes/albums/[id]/settings/+page.svelte index d5884be..debf745 100644 --- a/frontend/src/routes/albums/[id]/settings/+page.svelte +++ b/frontend/src/routes/albums/[id]/settings/+page.svelte @@ -2,6 +2,7 @@ import { page } from '$app/stores'; import { goto } from '$app/navigation'; import { api } from '$lib/api'; + import Icon from '$lib/components/Icon.svelte'; import type { Album, ShareLink } from '$lib/types'; const albumId = $derived($page.params.id); @@ -81,7 +82,10 @@ {#if error}

{error}

{/if} @@ -103,7 +107,13 @@ class="border-border bg-bg text-text w-full rounded-lg border px-3 py-2" > - + {#if saved}Сохранено{/if} @@ -117,9 +127,13 @@ class="border-border bg-bg text-text w-32 rounded-lg border px-2 py-1.5" /> - + + Создать ссылку +
    {#each shareLinks as link (link.id)} @@ -129,7 +143,14 @@ · /share/{link.token} {#if !link.revoked_at} - + {/if} {/each} @@ -138,6 +159,10 @@ + + Удалить альбом + + diff --git a/frontend/src/routes/invites/[token]/+page.svelte b/frontend/src/routes/invites/[token]/+page.svelte index b74c3ac..71b728e 100644 --- a/frontend/src/routes/invites/[token]/+page.svelte +++ b/frontend/src/routes/invites/[token]/+page.svelte @@ -2,6 +2,7 @@ import { page } from '$app/stores'; import { goto } from '$app/navigation'; import { api } from '$lib/api'; + import Icon from '$lib/components/Icon.svelte'; import { auth } from '$lib/stores/auth.svelte'; const token = $derived($page.params.token); @@ -43,13 +44,20 @@ type="button" disabled={busy} onclick={accept} - class="bg-primary text-primary-fg rounded-lg px-5 py-2.5 font-medium" + class="bg-primary text-primary-fg inline-flex items-center gap-2 rounded-lg px-5 py-2.5 font-medium" > + {busy ? 'Принятие…' : 'Принять приглашение'} {:else}

    Войдите или зарегистрируйтесь, чтобы принять приглашение.

    - Войти + + + Войти + {/if} {:else}

    Загрузка…

    diff --git a/frontend/src/routes/share/[token]/+page.svelte b/frontend/src/routes/share/[token]/+page.svelte index ee341d8..89e28cd 100644 --- a/frontend/src/routes/share/[token]/+page.svelte +++ b/frontend/src/routes/share/[token]/+page.svelte @@ -1,6 +1,7 @@