From ddda6fffa71ec890c0275d2730b2bfa60bfa1a32 Mon Sep 17 00:00:00 2001 From: Jordan Harrison Date: Sun, 6 Sep 2026 13:38:10 +0100 Subject: [PATCH 1/2] feat(ui): unify themes and polish mobile navigation --- .../(board)/member/[id]/reputation/page.tsx | 6 +- .../plugins/[key]/[[...path]]/page.tsx | 3 +- apps/community/app/(board)/search/page.tsx | 3 +- .../app/(board)/unsubscribe/page.tsx | 4 +- apps/community/app/admin/layout.tsx | 10 +- .../admin/plugins/[key]/[[...path]]/page.tsx | 4 +- apps/community/app/install/page.tsx | 6 +- apps/community/app/layout.tsx | 2 + .../components/account/active-sessions.tsx | 4 +- .../src/components/account/avatar-form.tsx | 5 +- .../src/components/account/feed-token.tsx | 10 +- .../components/account/notification-forms.tsx | 10 +- .../components/account/push-device-form.tsx | 5 +- .../components/account/reputation-forms.tsx | 9 +- .../components/account/sign-in-methods.tsx | 4 +- .../components/account/subscription-forms.tsx | 14 +- .../components/account/two-factor-forms.tsx | 7 +- .../src/components/account/usercp-forms.tsx | 107 +++++----- .../src/components/admin/admin-forms.tsx | 45 ++-- .../src/components/admin/form-bits.tsx | 5 +- .../src/components/admin/mail-test-card.tsx | 4 +- .../src/components/admin/theme-forms.tsx | 9 +- .../src/components/auth/form-controls.tsx | 33 ++- .../src/components/content/edit-post-form.tsx | 5 +- .../components/content/new-thread-form.tsx | 8 +- .../src/components/messages/message-forms.tsx | 19 +- .../moderation/inline-moderation-form.tsx | 5 +- .../src/components/moderation/queue-form.tsx | 6 +- .../components/moderation/report-forms.tsx | 6 +- .../moderation/thread-surgery-form.tsx | 8 +- .../moderation/thread-tools-form.tsx | 8 +- .../components/moderation/warning-forms.tsx | 16 +- .../src/components/shell/install-action.tsx | 5 +- .../shell/nav-disclosure-enhancer.tsx | 3 + .../src/components/shell/scheme-toggle.tsx | 2 +- .../src/components/shell/theme-switcher.tsx | 17 +- .../src/components/shell/view-tabs.tsx | 66 +----- apps/community/src/styles/globals.css | 63 ++++++ apps/community/src/view/copy.ts | 2 + apps/community/src/view/panel-nav.test.ts | 6 +- apps/community/src/view/panel-nav.ts | 18 +- docs/contributing/development.md | 27 +++ docs/customization/plugins.md | 17 ++ docs/customization/themes.md | 192 ++++++++++++------ docs/reference/theme-slots.md | 6 +- e2e/accessibility.spec.ts | 2 +- e2e/mobile-nav-no-js.spec.ts | 21 +- e2e/mobile-nav.spec.ts | 24 ++- e2e/passkeys.spec.ts | 6 +- e2e/screenshot-site.spec.ts | 2 +- e2e/screenshot-ui-polish.config.ts | 11 + e2e/screenshot-ui-polish.spec.ts | 171 ++++++++++++++++ e2e/support/session.ts | 8 +- e2e/two-factor.spec.ts | 4 +- e2e/ui-interactions.spec.ts | 176 ++++++++++++++++ e2e/ui-layout.spec.ts | 72 +++++++ packages/i18n/src/catalogs/en.json | 4 +- packages/plugin-kit/package.json | 3 +- packages/plugin-kit/src/ui.ts | 16 +- packages/theme-kit/src/view-models.ts | 20 +- packages/ui/package.json | 2 + packages/ui/src/card.tsx | 14 +- packages/ui/src/disclosure.tsx | 4 +- packages/ui/src/empty.tsx | 4 +- packages/ui/src/field.tsx | 31 +-- packages/ui/src/index.ts | 25 ++- packages/ui/src/nav-tabs-enhancer.test.ts | 102 ++++++++++ packages/ui/src/nav-tabs-enhancer.tsx | 64 ++++++ packages/ui/src/nav-tabs.test.ts | 33 +++ packages/ui/src/nav-tabs.tsx | 60 ++++++ packages/ui/src/navigation-drawer.tsx | 92 +++++++++ packages/ui/src/page-header.tsx | 56 +++++ packages/ui/src/password-input.test.ts | 26 +++ packages/ui/src/password-input.tsx | 61 ++++++ packages/ui/src/variants.ts | 65 +++++- playwright.config.ts | 2 +- plugins/calendar/package.json | 3 +- plugins/calendar/src/ui/admin.tsx | 14 +- plugins/calendar/src/ui/page.tsx | 49 ++--- plugins/dues/package.json | 3 +- plugins/dues/src/ui/admin.tsx | 52 +++-- plugins/dues/src/ui/pages.tsx | 22 +- pnpm-lock.yaml | 9 + scripts/i18n-baseline.json | 3 - themes/clubhouse/src/copy.ts | 3 +- themes/clubhouse/src/shared.tsx | 6 +- themes/clubhouse/src/slots/forum-row.tsx | 8 +- themes/clubhouse/src/slots/header.tsx | 106 +--------- themes/clubhouse/src/slots/thread-row.tsx | 8 +- themes/default/src/copy.ts | 26 ++- themes/default/src/index.ts | 3 + themes/default/src/messages/en.json | 11 + themes/default/src/shared.tsx | 18 +- themes/default/src/slots/auth-page.tsx | 17 +- themes/default/src/slots/board-index.tsx | 28 ++- themes/default/src/slots/category-block.tsx | 2 +- themes/default/src/slots/discovery-view.tsx | 63 +++--- themes/default/src/slots/error-notice.tsx | 4 +- themes/default/src/slots/footer.tsx | 2 +- themes/default/src/slots/forum-display.tsx | 20 +- themes/default/src/slots/forum-jump.tsx | 3 +- themes/default/src/slots/forum-row.tsx | 20 +- themes/default/src/slots/header.tsx | 112 +--------- .../default/src/slots/mobile-header-nav.tsx | 80 ++++++++ themes/default/src/slots/pagination.tsx | 4 +- themes/default/src/slots/panel-nav.tsx | 125 +++++++++--- themes/default/src/slots/panel-page.tsx | 18 +- themes/default/src/slots/panel-shell.tsx | 4 +- themes/default/src/slots/post-bit.tsx | 2 +- themes/default/src/slots/post-form.tsx | 6 +- themes/default/src/slots/redirect-notice.tsx | 4 +- themes/default/src/slots/search-results.tsx | 63 +++--- themes/default/src/slots/thread-row.tsx | 14 +- themes/default/src/slots/thread-view.tsx | 26 ++- themes/midnight/src/copy.ts | 3 +- themes/midnight/src/slots/header.tsx | 104 +--------- themes/phasebook/src/copy.ts | 5 +- themes/phasebook/src/slots/header.tsx | 104 +--------- themes/phasebook/src/slots/panel-nav.tsx | 19 +- themes/raidframe/src/copy.ts | 5 +- themes/raidframe/src/slots/header.tsx | 104 +--------- themes/raidframe/src/slots/panel-nav.tsx | 19 +- 122 files changed, 2076 insertions(+), 1243 deletions(-) create mode 100644 e2e/screenshot-ui-polish.config.ts create mode 100644 e2e/screenshot-ui-polish.spec.ts create mode 100644 e2e/ui-interactions.spec.ts create mode 100644 e2e/ui-layout.spec.ts create mode 100644 packages/ui/src/nav-tabs-enhancer.test.ts create mode 100644 packages/ui/src/nav-tabs-enhancer.tsx create mode 100644 packages/ui/src/nav-tabs.test.ts create mode 100644 packages/ui/src/nav-tabs.tsx create mode 100644 packages/ui/src/navigation-drawer.tsx create mode 100644 packages/ui/src/page-header.tsx create mode 100644 packages/ui/src/password-input.test.ts create mode 100644 packages/ui/src/password-input.tsx create mode 100644 themes/default/src/slots/mobile-header-nav.tsx diff --git a/apps/community/app/(board)/member/[id]/reputation/page.tsx b/apps/community/app/(board)/member/[id]/reputation/page.tsx index 7c918a512..856ae9f7e 100644 --- a/apps/community/app/(board)/member/[id]/reputation/page.tsx +++ b/apps/community/app/(board)/member/[id]/reputation/page.tsx @@ -1,7 +1,7 @@ import type { Metadata } from 'next' import { notFound } from 'next/navigation' -import { TextLink } from '@meith/ui' +import { PageTitle, TextLink } from '@meith/ui' import { RateMemberForm, WithdrawRatingForm } from '@/components/account/reputation-forms' import { BoardNotice } from '@/components/shell/board-notice' @@ -92,9 +92,7 @@ export default async function ReputationPage({ )}
-

- {profile.username}’s reputation -

+ {profile.username}’s reputation

{reputationLabel(view.summary, translator)}

diff --git a/apps/community/app/(board)/plugins/[key]/[[...path]]/page.tsx b/apps/community/app/(board)/plugins/[key]/[[...path]]/page.tsx index add429eb8..8ad7542ac 100644 --- a/apps/community/app/(board)/plugins/[key]/[[...path]]/page.tsx +++ b/apps/community/app/(board)/plugins/[key]/[[...path]]/page.tsx @@ -2,6 +2,7 @@ import type { Metadata } from 'next' import { notFound, redirect } from 'next/navigation' import { pluginPagePath } from '@meith/plugin-kit' +import { PageTitle } from '@meith/ui' import { boardUrl } from '@/server/board-url' import { getActor } from '@/server/context' @@ -59,7 +60,7 @@ export default async function PluginBoardPage({ tabIndex={-1} className="mx-auto flex w-full max-w-4xl flex-col gap-6 px-6 py-8 flex-1" > -

{result.title}

+ {result.title} {result.node === null ? (

{(await getTranslator()).t('board.plugin.failed')} diff --git a/apps/community/app/(board)/search/page.tsx b/apps/community/app/(board)/search/page.tsx index fc0bdb933..1a23310cf 100644 --- a/apps/community/app/(board)/search/page.tsx +++ b/apps/community/app/(board)/search/page.tsx @@ -22,6 +22,7 @@ import { type SearchFormModel, slotCopy, } from '@meith/theme-kit' +import { PageTitle } from '@meith/ui' import { SearchOffNotice } from '@/components/board/search-off-notice' import { getContainer } from '@/server/container' @@ -126,7 +127,7 @@ async function Page({ children }: { children: React.ReactNode }) { tabIndex={-1} className="mx-auto flex w-full max-w-3xl flex-col gap-6 px-6 py-8 flex-1" > -

{translator.t('board.search.title')}

+ {translator.t('board.search.title')} {children} ) diff --git a/apps/community/app/(board)/unsubscribe/page.tsx b/apps/community/app/(board)/unsubscribe/page.tsx index ed8a68281..c2c0cbcae 100644 --- a/apps/community/app/(board)/unsubscribe/page.tsx +++ b/apps/community/app/(board)/unsubscribe/page.tsx @@ -2,7 +2,7 @@ import type { Metadata } from 'next' import { env } from '@meith/core' import { readUnsubscribeToken } from '@meith/subscriptions' -import { TextLink } from '@meith/ui' +import { PageTitle, TextLink } from '@meith/ui' import { UnsubscribeConfirmForm } from '@/components/account/subscription-forms' import { BoardNotice } from '@/components/shell/board-notice' @@ -30,7 +30,7 @@ export default async function UnsubscribePage({ return (
-

{translator.t('page.unsubscribe')}

+ {translator.t('page.unsubscribe')} {done !== null ? ( <> diff --git a/apps/community/app/admin/layout.tsx b/apps/community/app/admin/layout.tsx index d9241160f..e9461ee3a 100644 --- a/apps/community/app/admin/layout.tsx +++ b/apps/community/app/admin/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from 'next' import { notFound } from 'next/navigation' import { ADMIN_IDLE_MINUTES } from '@meith/admin' +import { NavigationDrawerTrigger } from '@meith/ui' import { AdminSecondFactorForm, @@ -9,6 +10,7 @@ import { AdminSignOutForm, } from '@/components/admin/admin-forms' import { AdminNav } from '@/components/admin/admin-nav' +import { NavDisclosureEnhancer } from '@/components/shell/nav-disclosure-enhancer' import { PanelShell } from '@/components/shell/panel-shell' import { askForPassword, pendingAdminSecondFactor, resolveAdmin } from '@/server/admin' import { getActor } from '@/server/context' @@ -66,6 +68,7 @@ export default async function AdminLayout({ children }: { children: React.ReactN return (
+
-
diff --git a/apps/community/app/admin/plugins/[key]/[[...path]]/page.tsx b/apps/community/app/admin/plugins/[key]/[[...path]]/page.tsx index 886f2c7d6..36e84a3f8 100644 --- a/apps/community/app/admin/plugins/[key]/[[...path]]/page.tsx +++ b/apps/community/app/admin/plugins/[key]/[[...path]]/page.tsx @@ -72,9 +72,7 @@ export default async function AdminPluginPage({ {rendered.node === null ? (

{t.t('adminPluginDetail.pluginPageError')}

) : ( -
- {rendered.node} -
+
{rendered.node}
)} ) diff --git a/apps/community/app/install/page.tsx b/apps/community/app/install/page.tsx index b16655dc9..ea8a43feb 100644 --- a/apps/community/app/install/page.tsx +++ b/apps/community/app/install/page.tsx @@ -13,7 +13,7 @@ import { warnings, } from '@meith/install' import { isUsableOrigin, MAIL_PRESETS, normaliseOrigin } from '@meith/settings' -import { Alert, AlertDescription, AlertTitle, Disclosure } from '@meith/ui' +import { Alert, AlertDescription, AlertTitle, Disclosure, PageTitle } from '@meith/ui' import { InstallForm } from '@/components/install/install-form' import { InstallRestoreForm } from '@/components/install/restore-form' @@ -88,9 +88,7 @@ export default async function InstallPage() { return (
-

- {t.t('page.install-this-board')} -

+ {t.t('page.install-this-board')}

{t.t('page.this-page-works-once-when')}

diff --git a/apps/community/app/layout.tsx b/apps/community/app/layout.tsx index ba539a4b1..5a1aab7e1 100644 --- a/apps/community/app/layout.tsx +++ b/apps/community/app/layout.tsx @@ -5,6 +5,7 @@ import { env } from '@meith/core' import { currentRequestId } from '@meith/core/logger' import { localeDirection, SOURCE_LOCALE } from '@meith/i18n' import { resolveBoardUrl } from '@meith/settings' +import { NavTabsEnhancer } from '@meith/ui/nav-tabs-enhancer' import { CopyProvider } from '@/components/shell/copy' import { CrashNoticeProvider } from '@/components/shell/crash-notice' @@ -108,6 +109,7 @@ export default async function RootLayout({ + -