fixes: various bits fixed / improved based on fedback

This commit is contained in:
Starystars67
2025-12-29 16:16:17 +00:00
parent 31a179bc45
commit 6ed1bf7352
10 changed files with 198 additions and 51 deletions

12
package-lock.json generated
View File

@@ -1,19 +1,19 @@
{ {
"name": "beammp-website", "name": "beammp-website",
"version": "2.0.0", "version": "2.4.7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "beammp-website", "name": "beammp-website",
"version": "2.0.0", "version": "2.4.7",
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.1.17", "@tailwindcss/vite": "^4.1.17",
"@vueuse/core": "^14.1.0", "@vueuse/core": "^14.1.0",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-vue-next": "^0.555.0", "lucide-vue-next": "^0.555.0",
"reka-ui": "^2.6.0", "reka-ui": "^2.7.0",
"tailwind-merge": "^3.4.0", "tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.17", "tailwindcss": "^4.1.17",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
@@ -2832,9 +2832,9 @@
} }
}, },
"node_modules/reka-ui": { "node_modules/reka-ui": {
"version": "2.6.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.6.0.tgz", "resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.7.0.tgz",
"integrity": "sha512-NrGMKrABD97l890mFS3TNUzB0BLUfbL3hh0NjcJRIUSUljb288bx3Mzo31nOyUcdiiW0HqFGXJwyCBh9cWgb0w==", "integrity": "sha512-m+XmxQN2xtFzBP3OAdIafKq7C8OETo2fqfxcIIxYmNN2Ch3r5oAf6yEYCIJg5tL/yJU2mHqF70dCCekUkrAnXA==",
"dependencies": { "dependencies": {
"@floating-ui/dom": "^1.6.13", "@floating-ui/dom": "^1.6.13",
"@floating-ui/vue": "^1.1.6", "@floating-ui/vue": "^1.1.6",

View File

@@ -1,7 +1,7 @@
{ {
"name": "beammp-website", "name": "beammp-website",
"private": true, "private": true,
"version": "2.4.7", "version": "2.4.8",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -16,7 +16,7 @@
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-vue-next": "^0.555.0", "lucide-vue-next": "^0.555.0",
"reka-ui": "^2.6.0", "reka-ui": "^2.7.0",
"tailwind-merge": "^3.4.0", "tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.17", "tailwindcss": "^4.1.17",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",

View File

@@ -1,6 +1,14 @@
<script setup> <script setup>
import { Github, Facebook, Instagram, createLucideIcon } from 'lucide-vue-next' import { Github, Facebook, Instagram, createLucideIcon } from 'lucide-vue-next'
import { RouterLink } from 'vue-router' import { getLocalizedPath } from '@/utils/locale'
import { RouterLink, useRoute } from 'vue-router'
const route = useRoute()
// Generate localized route
function localRoute(path) {
return getLocalizedPath(path, route.params.locale)
}
const XIcon = createLucideIcon('X', [ const XIcon = createLucideIcon('X', [
[ [
@@ -183,7 +191,7 @@ const TikTokIcon = createLucideIcon('TikTok', [
<p>&copy; 2019 - {{ new Date().getFullYear() }} | BeamMP Mod Team All Rights Reserved</p> <p>&copy; 2019 - {{ new Date().getFullYear() }} | BeamMP Mod Team All Rights Reserved</p>
<div class="flex gap-3"> <div class="flex gap-3">
<RouterLink <RouterLink
to="/about" :to="localRoute('about')"
class="text-neutral-700 hover:text-beammp-blue transition-colors dark:text-neutral-500 dark:hover:text-blue-400" class="text-neutral-700 hover:text-beammp-blue transition-colors dark:text-neutral-500 dark:hover:text-blue-400"
> >
{{ $t('message.footer.about') }} {{ $t('message.footer.about') }}

View File

@@ -33,31 +33,30 @@ function localRoute(path) {
<template> <template>
<header <header
class="border-b border-neutral-200 dark:border-neutral-800 bg-white/80 dark:bg-neutral-900/70 backdrop-blur sticky top-0 z-20" class="border-b border-neutral-200 dark:border-neutral-800 bg-white/80 dark:bg-neutral-900/70 backdrop-blur sticky top-0 z-20 overflow-hidden"
> >
<nav class="max-w-6xl mx-auto px-4 h-16 flex items-center justify-between"> <nav class="max-w-7xl mx-auto px-3 sm:px-4 h-16 flex items-center justify-between gap-2 sm:gap-4">
<RouterLink <RouterLink
:to="localRoute('')" :to="localRoute('')"
class="flex items-center gap-2 shrink-0" class="flex items-center gap-2 flex-shrink-0"
@click="closeMobileMenu" @click="closeMobileMenu"
> >
<!-- Light mode logo (black) --> <!-- Light mode logo (black) -->
<img <img
src="/src/assets/BeamMP_blk.png" src="/src/assets/BeamMP_blk.png"
alt="BeamMP Logo" alt="BeamMP Logo"
class="h-16 w-auto shrink-0 dark:hidden" class="h-12 sm:h-14 w-auto flex-shrink-0 dark:hidden"
/> />
<!-- Dark mode logo (white) --> <!-- Dark mode logo (white) -->
<img <img
src="/src/assets/BeamMP_wht.png" src="/src/assets/BeamMP_wht.png"
alt="BeamMP Logo" alt="BeamMP Logo"
class="h-16 w-auto shrink-0 hidden dark:block" class="h-12 sm:h-14 w-auto flex-shrink-0 hidden dark:block"
/> />
</RouterLink> </RouterLink>
<!-- Desktop Navigation --> <!-- Desktop Navigation -->
<!-- Switch to mobile earlier (avoid logo crowding) --> <div class="hidden xl:flex items-center gap-1 flex-1 justify-end overflow-x-auto">
<div class="hidden lg:flex items-center gap-4">
<NavigationMenu> <NavigationMenu>
<NavigationMenuList> <NavigationMenuList>
<NavigationMenuItem> <NavigationMenuItem>
@@ -194,14 +193,14 @@ function localRoute(path) {
</NavigationMenuItem> </NavigationMenuItem>
</NavigationMenuList> </NavigationMenuList>
</NavigationMenu> </NavigationMenu>
<div class="flex items-center gap-2"> <div class="flex items-center gap-1 sm:gap-2 flex-shrink-0">
<LanguageSelector /> <LanguageSelector />
<ThemeToggle /> <ThemeToggle />
</div> </div>
</div> </div>
<!-- Mobile Menu Button and Theme Toggle --> <!-- Mobile Menu Button and Theme Toggle -->
<div class="flex lg:hidden items-center gap-2"> <div class="flex xl:hidden items-center gap-1 sm:gap-2 flex-shrink-0">
<LanguageSelector /> <LanguageSelector />
<ThemeToggle /> <ThemeToggle />
<button <button
@@ -226,7 +225,7 @@ function localRoute(path) {
> >
<div <div
v-if="mobileMenuOpen" v-if="mobileMenuOpen"
class="md:hidden border-t border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900" class="xl:hidden border-t border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900"
> >
<div class="px-4 py-3 space-y-1"> <div class="px-4 py-3 space-y-1">
<RouterLink <RouterLink
@@ -275,6 +274,15 @@ function localRoute(path) {
> >
{{ $t('message.nav.docs') }} {{ $t('message.nav.docs') }}
</a> </a>
<a
href="https://store.beammp.com"
target="_blank"
rel="noopener noreferrer"
class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors"
@click="closeMobileMenu"
>
{{ $t('message.nav.store') }}
</a>
<a <a
href="https://github.com/BeamMP/BeamMP" href="https://github.com/BeamMP/BeamMP"
target="_blank" target="_blank"

View File

@@ -0,0 +1,42 @@
<script setup>
import { reactiveOmit } from "@vueuse/core";
import { Check } from "lucide-vue-next";
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from "reka-ui";
import { cn } from "@/lib/utils";
const props = defineProps({
defaultValue: { type: [Boolean, String], required: false },
modelValue: { type: [Boolean, String, null], required: false },
disabled: { type: Boolean, required: false },
value: { type: null, required: false },
id: { type: String, required: false },
asChild: { type: Boolean, required: false },
as: { type: null, required: false },
name: { type: String, required: false },
required: { type: Boolean, required: false },
class: { type: null, required: false },
});
const emits = defineEmits(["update:modelValue"]);
const delegatedProps = reactiveOmit(props, "class");
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<CheckboxRoot
v-bind="forwarded"
:class="
cn(
'grid place-content-center peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
props.class,
)
"
>
<CheckboxIndicator class="grid place-content-center text-current">
<slot>
<Check class="h-4 w-4" />
</slot>
</CheckboxIndicator>
</CheckboxRoot>
</template>

View File

@@ -0,0 +1 @@
export { default as Checkbox } from "./Checkbox.vue";

View File

@@ -10,9 +10,10 @@
"forums": "Forums", "forums": "Forums",
"docs": "Docs", "docs": "Docs",
"communities": "Communities", "communities": "Communities",
"partners": "Partners", "partners": "Hosting Partners",
"servers": "Servers", "servers": "Servers",
"statistics": "Statistics", "statistics": "Statistics",
"store": "Merch Store",
"github": "GitHub", "github": "GitHub",
"patreon": "Patreon", "patreon": "Patreon",
"language": "Select Language", "language": "Select Language",

View File

@@ -1,7 +1,8 @@
<script setup> <script setup>
import { computed } from 'vue'
import { ExternalLink, Users, Trophy, Gamepad2, Shield, MapPin } from 'lucide-vue-next' import { ExternalLink, Users, Trophy, Gamepad2, Shield, MapPin } from 'lucide-vue-next'
const communities = [ const communities = computed(() => [
{ {
name: 'BeamMP Racing League', name: 'BeamMP Racing League',
description: description:
@@ -121,7 +122,7 @@ const communities = [
}, },
color: 'from-amber-600 to-yellow-600', color: 'from-amber-600 to-yellow-600',
}, },
] ])
</script> </script>
<template> <template>
@@ -250,7 +251,7 @@ const communities = [
<!-- CTA Section --> <!-- CTA Section -->
<section class="text-center py-16 bg-neutral-50 dark:bg-neutral-900/30 -mx-4 px-4 rounded-xl"> <section class="text-center py-16 bg-neutral-50 dark:bg-neutral-900/30 -mx-4 px-4 rounded-xl">
<div class="max-w-2xl mx-auto space-y-6"> <div class="max-w-2xl mx-auto space-y-6">
<h2 class="text-3xl md:text-4xl font-bold">{{ $t('message.communities.starting') }}</h2> <h2 class="text-3xl md:text-4xl font-bold">{{ $t('message.communities.starting.title') }}</h2>
<p class="text-lg text-neutral-600 dark:text-neutral-400"> <p class="text-lg text-neutral-600 dark:text-neutral-400">
{{ $t('message.communities.starting.description') }} {{ $t('message.communities.starting.description') }}
</p> </p>

View File

@@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import { import {
Download, Download,
Zap, Zap,
@@ -38,7 +38,7 @@ onMounted(async () => {
} }
}) })
const features = [ const features = computed(() => [
{ {
icon: Server, icon: Server,
title: t('message.home.features.stable_servers.title'), title: t('message.home.features.stable_servers.title'),
@@ -59,9 +59,9 @@ const features = [
title: t('message.home.features.sync.title'), title: t('message.home.features.sync.title'),
description: t('message.home.features.sync.description'), description: t('message.home.features.sync.description'),
}, },
] ])
const communities = [ const communities = computed(() => [
{ {
name: t('message.home.communities.racing.name'), name: t('message.home.communities.racing.name'),
icon: Rocket, icon: Rocket,
@@ -86,9 +86,9 @@ const communities = [
description: t('message.home.communities.freeroam.description'), description: t('message.home.communities.freeroam.description'),
color: 'from-green-500 to-emerald-500', color: 'from-green-500 to-emerald-500',
}, },
] ])
const devFeatures = [ const devFeatures = computed(() => [
{ {
icon: Code, icon: Code,
title: t('message.home.devFeatures.lua.title'), title: t('message.home.devFeatures.lua.title'),
@@ -96,7 +96,7 @@ const devFeatures = [
link: link:
'https://docs.beammp.com/' + 'https://docs.beammp.com/' +
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
(locale == 'en' ? '' : locale + '/') + (locale.value == 'en' ? '' : locale.value + '/') +
'scripting/mod-reference/', 'scripting/mod-reference/',
}, },
{ {
@@ -104,7 +104,7 @@ const devFeatures = [
title: t('message.home.devFeatures.docs.title'), title: t('message.home.devFeatures.docs.title'),
description: t('message.home.devFeatures.docs.description'), description: t('message.home.devFeatures.docs.description'),
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
link: 'https://docs.beammp.com' + (locale == 'en' ? '' : locale + '/'), link: 'https://docs.beammp.com/' + (locale.value == 'en' ? '' : locale.value + '/'),
}, },
{ {
icon: Wrench, icon: Wrench,
@@ -112,14 +112,14 @@ const devFeatures = [
description: t('message.home.devFeatures.openSource.description'), description: t('message.home.devFeatures.openSource.description'),
link: 'https://github.com/BeamMP', link: 'https://github.com/BeamMP',
}, },
] ])
const stats = [ const stats = computed(() => [
{ label: t('message.home.metrics.active_players'), value: onlinePlayers, suffix: '+' }, { label: t('message.home.metrics.active_players'), value: onlinePlayers, suffix: '+' },
{ label: t('message.home.metrics.public_servers'), value: '500', suffix: '+' }, { label: t('message.home.metrics.public_servers'), value: '500', suffix: '+' },
{ label: t('message.home.metrics.all_servers'), value: '2M', suffix: '+' }, { label: t('message.home.metrics.all_servers'), value: '2M', suffix: '+' },
] ])
const faqs = [ const faqs = computed(() => [
{ {
question: t('message.home.faq["0"].question'), question: t('message.home.faq["0"].question'),
answer: t('message.home.faq["0"].answer'), answer: t('message.home.faq["0"].answer'),
@@ -144,7 +144,7 @@ const faqs = [
question: t('message.home.faq["5"].question'), question: t('message.home.faq["5"].question'),
answer: t('message.home.faq["5"].answer'), answer: t('message.home.faq["5"].answer'),
}, },
] ])
</script> </script>
<template> <template>

View File

@@ -463,15 +463,17 @@ function joinServer(server) {
} }
.servers-filters { .servers-filters {
background: var(--card-bg, #f9f9f9); background: var(--filters-bg);
border: 1px solid var(--border-color, #e0e0e0); border: 1px solid var(--filters-border);
border-radius: 8px; border-radius: 12px;
padding: 20px; padding: 24px;
margin-bottom: 30px; margin-bottom: 30px;
box-shadow: var(--filters-shadow);
backdrop-filter: blur(10px);
} }
.filter-group { .filter-group {
margin-bottom: 15px; margin-bottom: 20px;
} }
.filter-group:last-child { .filter-group:last-child {
@@ -482,30 +484,87 @@ function joinServer(server) {
width: 100%; width: 100%;
padding: 12px 16px; padding: 12px 16px;
font-size: 1rem; font-size: 1rem;
border: 1px solid var(--border-color, #d0d0d0); border: 2px solid var(--border-color, #e0e0e0);
border-radius: 6px; border-radius: 8px;
background: var(--input-bg, #fff); background: var(--input-bg, #fff);
color: var(--text-color, #1a1a1a); color: var(--text-color, #1a1a1a);
transition: border-color 0.2s; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
font-weight: 500;
} }
.search-input:focus { .search-input:focus {
outline: none; outline: none;
border-color: var(--primary-color, #5d9cec); border-color: #ff6a00;
box-shadow: 0 0 0 3px rgba(255, 106, 0, 0.1);
}
.search-input::placeholder {
color: var(--placeholder-color, var(--text-muted, #999));
}
.filter-heading {
display: block;
margin-bottom: 12px;
font-size: 0.9rem;
font-weight: 600;
color: var(--text-color, #1a1a1a);
text-transform: uppercase;
letter-spacing: 0.5px;
opacity: 0.7;
} }
.filter-label { .filter-label {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
margin-right: 20px; margin-right: 24px;
margin-bottom: 12px;
font-size: 0.95rem; font-size: 0.95rem;
cursor: pointer; cursor: pointer;
color: var(--text-color, #1a1a1a); color: var(--text-color, #1a1a1a);
font-weight: 500;
transition: all 0.2s ease;
}
.filter-label:hover {
color: #ff6a00;
} }
.filter-label input[type='checkbox'] { .filter-label input[type='checkbox'] {
margin-right: 6px; appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 20px;
height: 20px;
margin-right: 10px;
cursor: pointer; cursor: pointer;
border: 2px solid var(--checkbox-border, #d0d0d0);
border-radius: 6px;
background: var(--checkbox-bg, #fff);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
flex-shrink: 0;
position: relative;
}
.filter-label input[type='checkbox']:hover {
border-color: #ff6a00;
box-shadow: 0 0 0 3px var(--checkbox-hover-shadow, rgba(255, 106, 0, 0.1));
}
.filter-label input[type='checkbox']:checked {
background: linear-gradient(135deg, #ff6a00 0%, #ff8c26 100%);
border-color: #ff6a00;
box-shadow: 0 2px 8px rgba(255, 106, 0, 0.3);
}
.filter-label input[type='checkbox']:checked::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-weight: bold;
font-size: 12px;
} }
.server-count { .server-count {
@@ -894,20 +953,27 @@ function joinServer(server) {
color: var(--text-muted, #666); color: var(--text-muted, #666);
} }
/* Dark mode support */ :global(.dark) .servers-container,
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.servers-container { .servers-container {
--text-color: #e0e0e0; --text-color: #e0e0e0;
--text-muted: #999; --text-muted: #999999;
--card-bg: #2a2a2a; --card-bg: #2a2a2a;
--border-color: #404040; --border-color: #404040;
--input-bg: #1a1a1a; --input-bg: #1a1a1a;
--tag-bg: #404040; --tag-bg: #404040;
--primary-color: #5d9cec; --primary-color: #5d9cec;
--header-bg: #1f1f1f; --header-bg: #1f1f1f;
--hover-bg: #333; --hover-bg: #333333;
--active-bg: #2a3f5f; --active-bg: #2a3f5f;
--details-bg: #252525; --details-bg: #252525;
--filters-bg: linear-gradient(135deg, rgba(30, 30, 30, 0.95) 0%, rgba(20, 20, 20, 0.98) 100%);
--filters-border: rgba(255, 106, 0, 0.2);
--filters-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
--checkbox-bg: rgba(255, 255, 255, 0.05);
--checkbox-border: rgba(255, 255, 255, 0.15);
--checkbox-hover-shadow: rgba(255, 106, 0, 0.15);
--placeholder-color: #666666;
} }
} }
@@ -963,3 +1029,23 @@ function joinServer(server) {
} }
} }
</style> </style>
/* Light theme defaults */
--text-color: #1a1a1a;
--text-muted: #666666;
--card-bg: #ffffff;
--border-color: #e0e0e0;
--input-bg: #ffffff;
--tag-bg: #e8e8e8;
--primary-color: #5d9cec;
--header-bg: #f5f5f5;
--hover-bg: #f9f9f9;
--active-bg: #f0f7ff;
--details-bg: #fafafa;
--filters-bg: linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(249, 249, 249, 0.95) 100%);
--filters-border: var(--border-color, #e0e0e0);
--filters-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
--checkbox-bg: #ffffff;
--checkbox-border: #d0d0d0;
--checkbox-hover-shadow: rgba(255, 106, 0, 0.1);
--placeholder-color: var(--text-muted, #999999);