Két tengelyen kap vissza életet a felület: piktogram és szín

A facelift után a site helyenként túl csendes lett. Élet kerül vissza, de
nem dekorációként: két szabály mentén, amiket egymástól függetlenül lehet
olvasni.

A piktogram identitást jelöl — miféle dolog ez. Minden motor és platform
saját glifát kap (TIC-80, Ebitengine, LÖVE, Godot, Bevy, Phaser, C64), és
ugyanazt a jelet viszi a katalógusban, a részletlapon és a build-mátrix
motor-oszlopában, egyetlen PlatformIcon komponensből. A Contact IRC- és
BBS-csatornája is jelet kapott, mert a másik három brandnek eddig is volt,
és a hiány látszott.

A szín állapotot jelöl — mi van vele. A négy státuszból három kap hangot:
a megjelent az akcens zöldet, a demó egy kéket (új --c-info token, 6,7:1
fehéren és 7,8:1 feketén), a fejlesztés alatti a borostyánt. Az archivált
marad semleges, mert az a "már nem történik vele semmi" állapot.

A navigáció, a gombok és a szekciócímek továbbra sem kapnak ikont: ott a
szöveg megmondja, mi az.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-20 16:02:47 +02:00
co-authored by Claude Opus 5
parent 02ffedbca2
commit 92b4ae22b7
10 changed files with 96 additions and 9 deletions
@@ -1,8 +1,11 @@
<template>
<span class="badge">{{ platform }}</span>
<span class="badge gap-1.5">
<PlatformIcon :platform="platform" />{{ platform }}
</span>
</template>
<script setup lang="ts">
import PlatformIcon from './PlatformIcon.vue'
defineProps<{ platform: string }>()
</script>
@@ -0,0 +1,26 @@
<template>
<BrandIcon v-if="brand" :name="brand" />
<component :is="glyph" v-else v-bind="ICON" />
</template>
<script setup lang="ts">
import { computed, type Component } from 'vue'
import { Bot, Box, Boxes, Heart, Save, Tv, Zap } from 'lucide-vue-next'
import BrandIcon from './icons/BrandIcon.vue'
import { ICON } from './icons'
const props = defineProps<{ platform: string }>()
const GLYPHS: Record<string, Component> = {
tic80: Tv,
love: Heart,
godot: Bot,
bevy: Boxes,
phaser: Zap,
c64: Save,
}
const key = computed(() => props.platform.toLowerCase().replace(/[^a-z0-9]/g, ''))
const brand = computed(() => (key.value === 'ebitengine' ? 'go' : null))
const glyph = computed(() => GLYPHS[key.value] ?? Box)
</script>
@@ -0,0 +1,27 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import PlatformBadge from '../PlatformBadge.vue'
import { statusBadgeClass } from '../../lib/softwareUtils'
describe('PlatformBadge', () => {
it('shows the platform name next to its own glyph', () => {
const wrapper = mount(PlatformBadge, { props: { platform: 'TIC-80' } })
expect(wrapper.text()).toContain('TIC-80')
expect(wrapper.find('svg').exists()).toBe(true)
})
it('falls back to a glyph for a platform it has never seen', () => {
const wrapper = mount(PlatformBadge, { props: { platform: 'Amiga' } })
expect(wrapper.find('svg').exists()).toBe(true)
})
})
describe('statusBadgeClass', () => {
it('gives every live status its own hue and leaves the archived ones neutral', () => {
expect(statusBadgeClass('released')).toContain('status-released')
expect(statusBadgeClass('demo')).toContain('status-demo')
expect(statusBadgeClass('development')).toContain('status-development')
expect(statusBadgeClass('archived')).toBe('status-badge')
expect(statusBadgeClass('whatever')).toBe('status-badge')
})
})
+2 -2
View File
@@ -32,8 +32,8 @@ export function getLatestStable(releases: Release[]): Release | undefined {
const STATUS_BADGE_CLASS: Record<string, string> = {
released: 'status-badge status-released',
demo: 'status-badge',
development: 'status-badge',
demo: 'status-badge status-demo',
development: 'status-badge status-development',
archived: 'status-badge',
}
@@ -40,7 +40,9 @@
</thead>
<tbody>
<tr v-for="(info, platform) in data.platforms" :key="platform" class="builds-row">
<td class="builds-td-engine">{{ info.label }}</td>
<td class="builds-td-engine">
<span class="builds-engine"><PlatformIcon :platform="platform as string" />{{ info.label }}</span>
</td>
<template v-for="group in columnGroups" :key="group.label">
<td
v-for="(col, colIdx) in group.kinds" :key="col.kind"
@@ -58,7 +60,9 @@
<div v-if="data" class="builds-mobile">
<div v-for="(info, platform) in data.platforms" :key="platform" class="builds-card">
<h2 class="builds-card-title">{{ info.label }}</h2>
<h2 class="builds-card-title">
<PlatformIcon :platform="platform as string" />{{ info.label }}
</h2>
<div v-for="group in columnGroups" :key="group.label" class="builds-card-group">
<h3 class="builds-card-group-header">{{ group.label }}</h3>
<div class="builds-card-kinds">
@@ -86,6 +90,7 @@ import { useI18n } from 'vue-i18n'
import { Check, X } from 'lucide-vue-next'
import { useBuildsStore } from '../../stores/builds.store'
import SkeletonCard from '../../components/SkeletonCard.vue'
import PlatformIcon from '../../components/PlatformIcon.vue'
const { t } = useI18n()
@@ -170,6 +175,10 @@ onMounted(() => store.fetch())
.builds-td-engine {
@apply whitespace-nowrap px-5 py-3 font-medium text-fg;
}
.builds-engine,
.builds-card-title {
@apply flex items-center gap-2;
}
.builds-td-kind {
@apply px-3 py-3 text-center;
}
@@ -32,7 +32,7 @@
</article>
<article class="channel">
<h3 class="channel-title">IRC</h3>
<h3 class="channel-title"><Terminal v-bind="ICON" />IRC</h3>
<p class="channel-desc">{{ t('contact.irc.desc') }}</p>
<dl class="channel-facts">
<dt>Network</dt><dd class="mono">Libera.Chat</dd>
@@ -41,7 +41,7 @@
</article>
<article class="channel">
<h3 class="channel-title">BBS</h3>
<h3 class="channel-title"><RadioTower v-bind="ICON" />BBS</h3>
<p class="channel-desc">{{ t('contact.bbs.desc') }}</p>
<dl class="channel-facts">
<dt>Host</dt><dd class="mono">games.teletype.hu</dd>
@@ -88,7 +88,8 @@
import { useI18n } from 'vue-i18n'
import { CONFIG } from '../../lib/config'
import MediaBand from '../../components/MediaBand.vue'
import { BrandIcon } from '../../components/icons'
import { RadioTower, Terminal } from 'lucide-vue-next'
import { BrandIcon, ICON } from '../../components/icons'
const { t } = useI18n()
+9 -1
View File
@@ -127,7 +127,15 @@ button:focus-visible {
}
.status-released {
@apply border-accent/40 bg-accent-quiet text-accent;
@apply border-accent/40 bg-accent/15 text-accent;
}
.status-demo {
@apply border-info/40 bg-info/15 text-info;
}
.status-development {
@apply border-warn/40 bg-warn/15 text-warn;
}
.mono {
+10
View File
@@ -229,6 +229,16 @@
background-color: #00a800 !important;
}
.retro-mode .status-demo {
background-color: #0000a8 !important;
color: #fff !important;
}
.retro-mode .status-development {
background-color: #a85400 !important;
color: #fff !important;
}
.retro-mode table {
border: 2px solid #a8a8a8 !important;
border-collapse: collapse !important;
+2
View File
@@ -15,6 +15,7 @@
--c-accent-fg: 255 255 255;
--c-accent-quiet: 220 252 231;
--c-info: 29 78 216;
--c-warn: 180 83 9;
--c-danger: 185 28 28;
}
@@ -36,6 +37,7 @@
--c-accent-fg: 9 9 11;
--c-accent-quiet: 20 83 45;
--c-info: 96 165 250;
--c-warn: 255 176 0;
--c-danger: 248 113 113;
}
+1
View File
@@ -21,6 +21,7 @@ module.exports = {
'accent-hover': token('--c-accent-hover'),
'accent-fg': token('--c-accent-fg'),
'accent-quiet': token('--c-accent-quiet'),
info: token('--c-info'),
warn: token('--c-warn'),
danger: token('--c-danger'),