Középre kerül, ami belépő pont, és sötéten indul a site
A facelift mindent balra igazított, pedig nem minden olvasnivaló. A szabály mostantól: középre az kerül, ami tájékozódási pont és nem szöveg — az oldalak hero-ja, a magányos állapotblokkok, és a lista végén álló egyetlen vissza-link. Balra marad minden, amit olvasnak vagy pásztáznak: törzsszöveg, listák, táblázatok, kártyák, vezérlők, lábléc. A részletoldalak kivételek: a blogbejegyzés és a katalógus-részlet hero-ja balra marad, mert ott a cím már a tartalom első sora, nem belépő pont. Az alapértelmezett téma a sötét lett; süti nélkül az indul, és a világosat az választja, aki kéri. Retro módban pedig eltűnik a téma-kapcsoló: ott a retro palettája írja felül a tokeneket, a gomb nem csinált volna semmit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,12 +8,9 @@
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<title>Teletype Games</title>
|
||||
<script>
|
||||
/* A témát a bundle betöltése ELŐTT tesszük a gyökérre, különben minden
|
||||
sötét témás oldalbetöltés villan egy világosat. A sütit ezután az
|
||||
ui.store tartja karban; süti nélkül az alapértelmezés a világos. */
|
||||
(function () {
|
||||
var m = document.cookie.match(/(?:^|; )theme=([^;]*)/)
|
||||
var theme = m && decodeURIComponent(m[1]) === 'dark' ? 'dark' : 'light'
|
||||
var theme = m && decodeURIComponent(m[1]) === 'light' ? 'light' : 'dark'
|
||||
document.documentElement.setAttribute('data-theme', theme)
|
||||
var r = document.cookie.match(/(?:^|; )retro-mode=([^;]*)/)
|
||||
document.documentElement.setAttribute('data-retro', r && r[1] === '1' ? '1' : '0')
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<button :class="localeBtnClass('hu')" class="locale-btn" @click="switchLocale('hu')">HU</button>
|
||||
</div>
|
||||
|
||||
<button class="icon-btn" :aria-label="t('nav.theme')" :title="t('nav.theme')" @click="toggleTheme">
|
||||
<button v-if="!isRetroMode" class="icon-btn" :aria-label="t('nav.theme')" :title="t('nav.theme')" @click="toggleTheme">
|
||||
<Sun v-if="theme === 'dark'" v-bind="ICON_CONTROL" />
|
||||
<Moon v-else v-bind="ICON_CONTROL" />
|
||||
</button>
|
||||
|
||||
@@ -46,12 +46,22 @@ describe('AppLayout', () => {
|
||||
|
||||
it('switches the theme from the header, and writes it to the root element', async () => {
|
||||
const wrapper = await mountLayout()
|
||||
expect(document.documentElement.getAttribute('data-theme')).toBe('light')
|
||||
expect(document.documentElement.getAttribute('data-theme')).toBe('dark')
|
||||
|
||||
await wrapper.findAll('.icon-btn')[0].trigger('click')
|
||||
|
||||
expect(document.documentElement.getAttribute('data-theme')).toBe('dark')
|
||||
expect(document.cookie).toContain('theme=dark')
|
||||
expect(document.documentElement.getAttribute('data-theme')).toBe('light')
|
||||
expect(document.cookie).toContain('theme=light')
|
||||
})
|
||||
|
||||
it('drops the theme switch in retro mode, where it would do nothing', async () => {
|
||||
const wrapper = await mountLayout()
|
||||
expect(wrapper.findAll('.icon-btn')).toHaveLength(2)
|
||||
|
||||
await wrapper.get('.retro-toggle-btn').trigger('click')
|
||||
|
||||
expect(wrapper.findAll('.icon-btn')).toHaveLength(1)
|
||||
expect(wrapper.get('.icon-btn').attributes('aria-label')).toBe('Menu')
|
||||
})
|
||||
|
||||
it('marks retro mode on the root element too, so the page background follows', async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="blog-container">
|
||||
<header class="hero-section">
|
||||
<div class="hero-container">
|
||||
<div class="hero-container hero-centered">
|
||||
<p class="hero-badge">{{ t('blog.badge') }}</p>
|
||||
<h1 class="hero-title">{{ t('blog.title') }}</h1>
|
||||
<p class="hero-subtitle">{{ t('blog.subtitle') }}</p>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<header class="hero-section">
|
||||
<div class="hero-container">
|
||||
<div class="hero-container hero-centered">
|
||||
<h1 class="hero-title">{{ t('builds.title') }}</h1>
|
||||
<p class="hero-subtitle">{{ t('builds.subtitle') }}</p>
|
||||
</div>
|
||||
@@ -72,7 +72,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="builds-back">
|
||||
<div class="focus-row">
|
||||
<RouterLink to="/catalog" class="link text-sm">{{ t('catalogShow.back') }}</RouterLink>
|
||||
</div>
|
||||
</main>
|
||||
@@ -202,7 +202,4 @@ onMounted(() => store.fetch())
|
||||
.builds-card-kind {
|
||||
@apply flex items-center gap-1.5 text-sm text-fg-muted;
|
||||
}
|
||||
.builds-back {
|
||||
@apply mt-8;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<header class="hero-section">
|
||||
<div class="hero-container">
|
||||
<div class="hero-container hero-centered">
|
||||
<h1 class="hero-title">{{ t('catalog.title') }}</h1>
|
||||
<p class="hero-subtitle">{{ t('catalog.subtitle') }}</p>
|
||||
<div class="hero-actions">
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
</main>
|
||||
|
||||
<main v-else class="main-container">
|
||||
<p class="text-fg-muted">{{ error || t('catalogShow.loading') }}</p>
|
||||
<p class="state-message">{{ error || t('catalogShow.loading') }}</p>
|
||||
</main>
|
||||
|
||||
<ImageLightbox
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<header class="hero-section">
|
||||
<div class="hero-container">
|
||||
<div class="hero-container hero-centered">
|
||||
<h1 class="hero-title">{{ t('code.title') }}</h1>
|
||||
<p class="hero-subtitle">{{ t('code.subtitle') }}</p>
|
||||
<div class="hero-actions">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<header class="hero-section">
|
||||
<div class="hero-container">
|
||||
<div class="hero-container hero-centered">
|
||||
<h1 class="hero-title">{{ t('contact.title') }}</h1>
|
||||
<p class="hero-subtitle">{{ t('contact.subtitle') }}</p>
|
||||
<div class="hero-actions">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="engines-container">
|
||||
<header class="hero-section">
|
||||
<div class="hero-container">
|
||||
<div class="hero-container hero-centered">
|
||||
<p class="hero-badge">{{ t('engines.badge') }}</p>
|
||||
<h1 class="hero-title">{{ t('engines.titleLead') }} {{ t('engines.titleAccent') }}</h1>
|
||||
<p class="hero-subtitle">{{ t('engines.subtitle') }}</p>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="home-container">
|
||||
<header class="hero-section">
|
||||
<div class="hero-container">
|
||||
<div class="hero-container hero-centered">
|
||||
<p class="hero-badge">{{ t('home.welcomeTo') }}</p>
|
||||
<h1 class="hero-title">Teletype <span class="text-accent">Games</span></h1>
|
||||
<p class="hero-subtitle">{{ t('home.subtitle') }}</p>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="howtos-container">
|
||||
<header class="hero-section">
|
||||
<div class="hero-container">
|
||||
<div class="hero-container hero-centered">
|
||||
<p class="hero-badge">{{ t('howtos.badge') }}</p>
|
||||
<h1 class="hero-title">{{ t('howtos.title') }}</h1>
|
||||
<p class="hero-subtitle">{{ t('howtos.subtitle') }}</p>
|
||||
@@ -49,7 +49,7 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div v-if="!error && recentPages.length > 0" class="howtos-footer">
|
||||
<div v-if="!error && recentPages.length > 0" class="focus-row">
|
||||
<a :href="WIKI_BASE" target="_blank" rel="noopener noreferrer" class="link text-sm">
|
||||
{{ t('howtos.browseWiki') }}
|
||||
</a>
|
||||
@@ -101,7 +101,4 @@ onMounted(() => store.fetch())
|
||||
.howto-card-footer {
|
||||
@apply mt-auto truncate border-t border-line pt-3 font-mono text-xs text-fg-subtle;
|
||||
}
|
||||
.howtos-footer {
|
||||
@apply mt-10;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<header class="hero-section">
|
||||
<div class="hero-container">
|
||||
<div class="hero-container hero-centered">
|
||||
<h1 class="hero-title">{{ t('stores.title') }}</h1>
|
||||
<p class="hero-subtitle">{{ t('stores.subtitle') }}</p>
|
||||
</div>
|
||||
@@ -105,7 +105,7 @@
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<div class="st-back">
|
||||
<div class="focus-row">
|
||||
<RouterLink to="/catalog" class="link text-sm">{{ t('catalogShow.back') }}</RouterLink>
|
||||
</div>
|
||||
</main>
|
||||
@@ -280,7 +280,4 @@ const platforms = [
|
||||
.st-platform-ext {
|
||||
@apply rounded border border-line bg-surface px-2 py-0.5 text-xs text-accent;
|
||||
}
|
||||
.st-back {
|
||||
@apply mt-8;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<header class="hero-section">
|
||||
<div class="hero-container">
|
||||
<div class="hero-container hero-centered">
|
||||
<h1 class="hero-title">{{ t('team.title') }}</h1>
|
||||
<p class="hero-subtitle">{{ t('team.subtitle') }}</p>
|
||||
</div>
|
||||
|
||||
@@ -23,28 +23,28 @@ describe('useUiStore', () => {
|
||||
expect(store.isRetroMode).toBe(false)
|
||||
})
|
||||
|
||||
it('starts in the light theme when no cookie is set', () => {
|
||||
it('starts in the dark theme when no cookie is set', () => {
|
||||
const store = useUiStore()
|
||||
expect(store.theme).toBe('dark')
|
||||
expect(document.documentElement.getAttribute('data-theme')).toBe('dark')
|
||||
})
|
||||
|
||||
it('reads the theme back from the cookie', () => {
|
||||
document.cookie = 'theme=light;path=/'
|
||||
const store = useUiStore()
|
||||
expect(store.theme).toBe('light')
|
||||
expect(document.documentElement.getAttribute('data-theme')).toBe('light')
|
||||
})
|
||||
|
||||
it('reads the theme back from the cookie', () => {
|
||||
document.cookie = 'theme=dark;path=/'
|
||||
const store = useUiStore()
|
||||
expect(store.theme).toBe('dark')
|
||||
expect(document.documentElement.getAttribute('data-theme')).toBe('dark')
|
||||
})
|
||||
|
||||
it('toggling the theme writes the cookie and the root attribute', () => {
|
||||
const store = useUiStore()
|
||||
store.toggleTheme()
|
||||
expect(store.theme).toBe('dark')
|
||||
expect(document.cookie).toContain('theme=dark')
|
||||
expect(document.documentElement.getAttribute('data-theme')).toBe('dark')
|
||||
store.toggleTheme()
|
||||
expect(store.theme).toBe('light')
|
||||
expect(document.cookie).toContain('theme=light')
|
||||
expect(document.documentElement.getAttribute('data-theme')).toBe('light')
|
||||
store.toggleTheme()
|
||||
expect(store.theme).toBe('dark')
|
||||
expect(document.cookie).toContain('theme=dark')
|
||||
})
|
||||
|
||||
it('toggles menu', () => {
|
||||
|
||||
@@ -7,7 +7,7 @@ export type Theme = 'light' | 'dark'
|
||||
const THEME_COOKIE = 'theme'
|
||||
|
||||
function readTheme(): Theme {
|
||||
return getCookie(THEME_COOKIE) === 'dark' ? 'dark' : 'light'
|
||||
return getCookie(THEME_COOKIE) === 'light' ? 'light' : 'dark'
|
||||
}
|
||||
|
||||
function applyTheme(theme: Theme) {
|
||||
|
||||
@@ -65,6 +65,26 @@ button:focus-visible {
|
||||
@apply mt-6 flex flex-wrap items-center gap-3;
|
||||
}
|
||||
|
||||
.hero-centered {
|
||||
@apply text-center;
|
||||
}
|
||||
|
||||
.hero-centered .hero-subtitle {
|
||||
@apply mx-auto;
|
||||
}
|
||||
|
||||
.hero-centered .hero-actions {
|
||||
@apply justify-center;
|
||||
}
|
||||
|
||||
.focus-row {
|
||||
@apply mt-8 text-center;
|
||||
}
|
||||
|
||||
.state-message {
|
||||
@apply py-16 text-center text-fg-muted;
|
||||
}
|
||||
|
||||
.card {
|
||||
@apply rounded-card border border-line bg-surface p-5 md:p-6;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user