feat: nový vzhled My Little Pony G5
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 27s
CI / Build server (push) Successful in 28s
CI / Build client (push) Successful in 41s
CI / Playwright E2E tests (push) Successful in 1m56s
CI / Build and push Docker image (push) Successful in 47s
CI / Notify (push) Successful in 2s

This commit is contained in:
mates committed 2026-10-25 20:18:36 +01:00
1 parent 67803d2d4e
commit 6fde8f3ec1
10 files changed
+911 -7

No files matched your search

+2 -2
View File
@@ -11,7 +11,7 @@
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c:wght@500;800&family=Nunito:wght@600;800;900&family=Fredoka:wght@500;600;700&family=Shippori+Mincho:wght@600;700;800&family=Zen+Maru+Gothic:wght@500;700;900&family=Yusei+Magic&family=Dela+Gothic+One&family=Barlow+Condensed:wght@500;600;700&family=Kiwi+Maru:wght@400;500&family=Playfair+Display:ital,wght@0,800;1,800&family=Courier+Prime:wght@700&family=Cormorant+Garamond:wght@600;700&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c:wght@500;800&family=Nunito:wght@600;800;900&family=Fredoka:wght@500;600;700&family=Shippori+Mincho:wght@600;700;800&family=Zen+Maru+Gothic:wght@500;700;900&family=Yusei+Magic&family=Dela+Gothic+One&family=Barlow+Condensed:wght@500;600;700&family=Kiwi+Maru:wght@400;500&family=Playfair+Display:ital,wght@0,800;1,800&family=Courier+Prime:wght@700&family=Cormorant+Garamond:wght@600;700&family=Baloo+2:wght@600;800&display=swap" rel="stylesheet" />
<title>Luncher</title> <title>Luncher</title>
<script> <script>
(function() { (function() {
@@ -28,7 +28,7 @@
} }
document.documentElement.setAttribute('data-bs-theme', theme); document.documentElement.setAttribute('data-bs-theme', theme);
var style = localStorage.getItem('style_theme'); var style = localStorage.getItem('style_theme');
var styles = ['umamusume', 'mlp', 'hellgirl', 'kon', 'bocchi', 'evangelion', 'yurucamp', 'spyfamily', 'apothecary', 'frieren']; var styles = ['umamusume', 'mlp', 'hellgirl', 'kon', 'bocchi', 'evangelion', 'yurucamp', 'spyfamily', 'apothecary', 'frieren', 'mlpg5'];
document.documentElement.setAttribute('data-luncher-theme', styles.indexOf(style) >= 0 ? style : 'simple'); document.documentElement.setAttribute('data-luncher-theme', styles.indexOf(style) >= 0 ? style : 'simple');
} catch (e) { } catch (e) {
// Fallback pokud localStorage není dostupný // Fallback pokud localStorage není dostupný
+1
View File
@@ -20,6 +20,7 @@ import './themes/yurucamp.scss';
import './themes/spyfamily.scss'; import './themes/spyfamily.scss';
import './themes/apothecary.scss'; import './themes/apothecary.scss';
import './themes/frieren.scss'; import './themes/frieren.scss';
import './themes/mlpg5.scss';
import { faCircleCheck, faNoteSticky, faTrashCan, faComment } from '@fortawesome/free-regular-svg-icons'; import { faCircleCheck, faNoteSticky, faTrashCan, faComment } from '@fortawesome/free-regular-svg-icons';
import { useSettings } from './context/settings'; import { useSettings } from './context/settings';
import Footer from './components/Footer'; import Footer from './components/Footer';
+13
View File
@@ -127,6 +127,19 @@
box-shadow: 0 0 0 2px #8e7cc3, 0 0 8px rgb(179 163 234 / 0.7); box-shadow: 0 0 0 2px #8e7cc3, 0 0 8px rgb(179 163 234 / 0.7);
} }
// Styl My Little Pony G5: emoji v bílém kruhu s duhovým lemem Mane 5
.eater-badge-mlpg5 {
display: inline-grid;
place-items: center;
font-size: 15px;
line-height: 1;
border-color: transparent;
background:
radial-gradient(circle at 40% 35%, #fff, #ffeef6) padding-box,
linear-gradient(135deg, #f08a3c, #ee5fa8, #9b6fe0, #2fa89c) border-box;
box-shadow: 0 2px 6px rgb(238 95 168 / 0.45);
}
// Odznak a jméno drží na jednom řádku (úzký sloupec „Člen" na stránce objednávek) // Odznak a jméno drží na jednom řádku (úzký sloupec „Člen" na stránce objednávek)
.user-info:has(.eater-badge) { .user-info:has(.eater-badge) {
display: inline-flex; display: inline-flex;
+8 -1
View File
@@ -67,6 +67,12 @@ const FR_LEVELS = [
{ minItems: 2, emoji: '🍖', title: 'Úroveň Stark — obří hamburger steak, který mu k narozeninám udělala Fern' }, { minItems: 2, emoji: '🍖', title: 'Úroveň Stark — obří hamburger steak, který mu k narozeninám udělala Fern' },
]; ];
/** Úrovně ve stylu My Little Pony G5 — emoji v kruhu s duhovým lemem Mane 5. Seřazeno od nejvyšší úrovně. */
const G5_LEVELS = [
{ minItems: 3, emoji: '🐉', title: 'Úroveň Sparky — malý drak sní všechno, i Hitchovu svačinu' },
{ minItems: 2, emoji: '🥤', title: 'Úroveň Sunny — k obědu ještě smoothie z jejího stánku' },
];
/** Úrovně podle stylu pro odznaky s emoji. */ /** Úrovně podle stylu pro odznaky s emoji. */
const EMOJI_LEVELS: Partial<Record<StyleTheme, typeof MLP_LEVELS>> = { const EMOJI_LEVELS: Partial<Record<StyleTheme, typeof MLP_LEVELS>> = {
mlp: MLP_LEVELS, mlp: MLP_LEVELS,
@@ -78,6 +84,7 @@ const EMOJI_LEVELS: Partial<Record<StyleTheme, typeof MLP_LEVELS>> = {
spyfamily: SXF_LEVELS, spyfamily: SXF_LEVELS,
apothecary: APO_LEVELS, apothecary: APO_LEVELS,
frieren: FR_LEVELS, frieren: FR_LEVELS,
mlpg5: G5_LEVELS,
}; };
type Props = { type Props = {
@@ -85,7 +92,7 @@ type Props = {
items: number, items: number,
}; };
/** Odznak s postavou u strávníků s více jídly — jen ve stylech Umamusume, My Little Pony, Hell Girl, K-ON!, Bocchi the Rock!, Evangelion, Yuru Camp△, Spy × Family, Apothecary Diaries a Frieren. */ /** Odznak s postavou u strávníků s více jídly — jen ve stylech Umamusume, My Little Pony, Hell Girl, K-ON!, Bocchi the Rock!, Evangelion, Yuru Camp△, Spy × Family, Apothecary Diaries, Frieren a My Little Pony G5. */
export default function EaterBadge({ items }: Readonly<Props>) { export default function EaterBadge({ items }: Readonly<Props>) {
const styleTheme = useSettings()?.styleTheme; const styleTheme = useSettings()?.styleTheme;
const isUma = styleTheme === 'umamusume'; const isUma = styleTheme === 'umamusume';
+75
View File
@@ -883,3 +883,78 @@
} }
} }
} }
// ============================================
// Konfety ve stylu My Little Pony (G5)
// ============================================
.confetti-g5 {
filter: drop-shadow(0 4px 4px rgb(43 36 64 / 0.22));
}
// Samolepky jako z Pippina kanálu — buclaté, s bílým okrajem a 3D spodkem
.confetti-g5-badge {
padding: 4px 16px;
border-radius: 999px;
border: 3px solid #fff;
color: #fff;
font-family: 'Baloo 2', 'Nunito', sans-serif;
font-weight: 800;
font-size: 1.15rem;
white-space: nowrap;
text-shadow: 0 1px 0 rgb(0 0 0 / 0.2);
box-shadow: 0 4px 0 rgb(0 0 0 / 0.15);
}
// Nápis „Make Your Mark!" — duhová pilulka Mane 5, vyskočí a zhoupne se jako bublina
.confetti-g5-banner {
position: absolute;
top: 38%;
left: 50%;
transform: translate(-50%, -50%);
padding: 10px 44px 6px;
font-family: 'Baloo 2', 'Nunito', sans-serif;
font-weight: 800;
font-size: clamp(1.8rem, 7vw, 4rem);
white-space: nowrap;
color: #fff;
text-shadow: 0 3px 0 rgb(0 0 0 / 0.18);
background: linear-gradient(90deg, #f08a3c, #ee5fa8, #9b6fe0, #5a7bd8, #2fa89c);
border: 5px solid #fff;
border-radius: 999px;
box-shadow: 0 8px 0 rgb(0 0 0 / 0.12), 0 18px 40px rgb(238 95 168 / 0.45);
animation: g5-banner 2.6s cubic-bezier(0.3, 1.4, 0.5, 1) both;
}
@keyframes g5-banner {
0% { transform: translate(-50%, -50%) scale(0); opacity: 0; }
14% { transform: translate(-50%, -50%) scale(1.12) rotate(-3deg); opacity: 1; }
24% { transform: translate(-50%, -50%) scale(0.96) rotate(2deg); }
34%, 80% { transform: translate(-50%, -50%) scale(1) rotate(0deg); opacity: 1; }
100% { transform: translate(-50%, -50%) scale(0.85); opacity: 0; }
}
// Postava s hláškou — kruh s duhovým lemem a bublina jako komentář pod příspěvkem
.confetti-mlpg5-voice {
.confetti-mlp-avatar {
border: 4px solid #fff;
background:
radial-gradient(circle at 40% 35%, #fff, #ffeef6) padding-box,
linear-gradient(135deg, #f08a3c, #ee5fa8, #9b6fe0, #2fa89c) border-box;
border-color: transparent;
box-shadow: 0 0 0 3px var(--mlp-speaker, #ee5fa8), 0 6px 14px rgb(0 0 0 / 0.25);
}
.confetti-mlp-bubble {
background: #fff;
color: #2b2440;
border: 3px solid var(--mlp-speaker, #ee5fa8);
border-radius: 20px;
box-shadow: 0 5px 0 var(--mlp-speaker, #ee5fa8);
font-family: 'Baloo 2', 'Nunito', sans-serif;
p {
font-weight: 700;
font-size: 1.05rem;
}
}
}
+17 -1
View File
@@ -151,6 +151,21 @@ const FR_SPEAKERS = [
{ name: 'Aura', emoji: '⚖️', color: '#c76a96', quote: 'Poklekněte před tímto obědem! Váhy rozhodly.' }, { name: 'Aura', emoji: '⚖️', color: '#c76a96', quote: 'Poklekněte před tímto obědem! Váhy rozhodly.' },
{ name: 'Mimik', emoji: '📦', color: '#8a6a3a', quote: '(cvak!) …(spokojeně žvýká Frieren)' }, { name: 'Mimik', emoji: '📦', color: '#8a6a3a', quote: '(cvak!) …(spokojeně žvýká Frieren)' },
]; ];
const G5_EMOJI = ['☀️', '🦄', '🌈', '💎', '📱', '❤️', '🥤', '🐉', '⭐', '🌊', '🎨', '🔍', '🌺', '✨'];
// Hlášky z My Little Pony G5 (Make Your Mark, Pippsqueaks, Maretime Bay, Zephyr Heights, Bridlewood...)
const G5_BADGES = ['Make Your Mark!', 'Fit Right In', 'Pippsqueaks ♡', 'Maretime Bay', 'Zephyr Heights', 'Bridlewood', '♥ Like!', 'Smoothie time!', 'Šerif Hitch', 'Unity!'];
const G5_BADGE_COLORS = ['#f08a3c', '#ee5fa8', '#9b6fe0', '#5a7bd8', '#2fa89c', '#6fb1d6'];
/** Mane 5, Misty, Sparky a královna Haven s „hláškou" po finálním výsledku. */
const G5_SPEAKERS = [
{ name: 'Sunny Starscout', emoji: '☀️', color: '#f08a3c', quote: 'Super! Kdo jde se mnou? Po obědě dáme smoothie!' },
{ name: 'Izzy Moonbow', emoji: '🦄', color: '#9b6fe0', quote: 'Jééé! A z obalu si potom něco vyrobím — upcycling!' },
{ name: 'Pipp Petals', emoji: '📱', color: '#ee5fa8', quote: 'Ahoj, Pippsqueaks! Dneska vám ukážu ten NEJLEPŠÍ oběd. Lajkujte!' },
{ name: 'Zipp Storm', emoji: '🔍', color: '#5a7bd8', quote: 'Vyšetřila jsem to. Všechny stopy vedou k tomuhle podniku.' },
{ name: 'Hitch Trailblazer', emoji: '⭐', color: '#2fa89c', quote: 'Jako šerif Maretime Bay to schvaluju. Sparky, nesahej mi na talíř!' },
{ name: 'Misty Brightdawn', emoji: '💙', color: '#6fb1d6', quote: 'J-já… já bych to taky ráda zkusila. Jestli teda můžu.' },
{ name: 'Sparky Sparkeroni', emoji: '🐉', color: '#7cb342', quote: '(škytne a vypustí obláček duhových jisker)' },
{ name: 'Královna Haven', emoji: '👑', color: '#d4a21e', quote: 'Královská volba, mé drahé. Zephyr Heights by souhlasily.' },
];
/** Styly, jejichž konfety tvoří emoji a samolepky s hláškami (a po finále promluví postava s emoji). */ /** Styly, jejichž konfety tvoří emoji a samolepky s hláškami (a po finále promluví postava s emoji). */
const EMOJI_CONFETTI: Partial<Record<WheelTheme, { const EMOJI_CONFETTI: Partial<Record<WheelTheme, {
prefix: string, badges: string[], colors: string[], emoji: string[], prefix: string, badges: string[], colors: string[], emoji: string[],
@@ -166,6 +181,7 @@ const EMOJI_CONFETTI: Partial<Record<WheelTheme, {
spyfamily: { prefix: 'confetti-sxf', badges: SXF_BADGES, colors: SXF_BADGE_COLORS, emoji: SXF_EMOJI, speakers: SXF_SPEAKERS, banner: '🥜 わくわく! 🥜' }, spyfamily: { prefix: 'confetti-sxf', badges: SXF_BADGES, colors: SXF_BADGE_COLORS, emoji: SXF_EMOJI, speakers: SXF_SPEAKERS, banner: '🥜 わくわく! 🥜' },
apothecary: { prefix: 'confetti-apo', badges: APO_BADGES, colors: APO_BADGE_COLORS, emoji: APO_EMOJI, speakers: APO_SPEAKERS, banner: '🏮 薬屋のひとりごと 🏮' }, apothecary: { prefix: 'confetti-apo', badges: APO_BADGES, colors: APO_BADGE_COLORS, emoji: APO_EMOJI, speakers: APO_SPEAKERS, banner: '🏮 薬屋のひとりごと 🏮' },
frieren: { prefix: 'confetti-fr', badges: FR_BADGES, colors: FR_BADGE_COLORS, emoji: FR_EMOJI, speakers: FR_SPEAKERS, banner: '✦ 葬送のフリーレン ✦' }, frieren: { prefix: 'confetti-fr', badges: FR_BADGES, colors: FR_BADGE_COLORS, emoji: FR_EMOJI, speakers: FR_SPEAKERS, banner: '✦ 葬送のフリーレン ✦' },
mlpg5: { prefix: 'confetti-g5', badges: G5_BADGES, colors: G5_BADGE_COLORS, emoji: G5_EMOJI, speakers: G5_SPEAKERS, banner: '☀️ Make Your Mark! ☀️' },
}; };
const DURATION_MS = 6400; const DURATION_MS = 6400;
@@ -319,7 +335,7 @@ export default function WheelConfetti({ theme, burstKey, big = false }: Readonly
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [burstKey]); }, [burstKey]);
// Ostatní tematické styly: hlášku pronese náhodná postava (Mane 6, Ai Enma, Houkago Tea Time, Kessoku Band, piloti NERV, Nokuru, Forgerovi, vnitřní palác, družina Frieren...) // Ostatní tematické styly: hlášku pronese náhodná postava (Mane 6, Ai Enma, Houkago Tea Time, Kessoku Band, piloti NERV, Nokuru, Forgerovi, vnitřní palác, družina Frieren, poníci z Maretime Bay...)
const emojiConfetti = EMOJI_CONFETTI[theme]; const emojiConfetti = EMOJI_CONFETTI[theme];
const emojiSpeaker = useMemo( const emojiSpeaker = useMemo(
() => big && emojiConfetti ? pick(emojiConfetti.speakers) : undefined, () => big && emojiConfetti ? pick(emojiConfetti.speakers) : undefined,
@@ -2855,6 +2855,256 @@
} }
} }
// ============================================
// STYL MY LITTLE PONY (G5) kola štěstí (dle stylu zvoleného v nastavení).
// Výseče v barvách Mane 5 a Misty, bílé linky a střed jako sluníčko Sunny.
// ============================================
.spin-wheel-modal.spin-theme-mlpg5 {
--g5-font: 'Nunito', sans-serif;
--g5-display: 'Baloo 2', 'Nunito', sans-serif;
--g5-sunny: #f08a3c;
--g5-sunny-deep: #c85a14;
--g5-pipp: #ee5fa8;
--g5-hitch: #2fa89c;
--g5-line: #f3dccb;
--g5-panel: #ffffff;
--g5-text: #2b2440;
--wheel-c0: #f08a3c;
--wheel-c1: #ee5fa8;
--wheel-c2: #9b6fe0;
--wheel-c3: #5a7bd8;
--wheel-c4: #2fa89c;
--wheel-c5: #6fb1d6;
--wheel-rim: #1f8f9a;
--wheel-hub: #ffd23f;
font-family: var(--g5-font);
.modal-content {
border: none;
border-radius: 26px;
overflow: hidden;
background: linear-gradient(180deg, #e2f6f6 0, #fff8f1 200px);
color: var(--g5-text);
}
.modal-header {
background:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='12'%3E%3Cpath d='M0 12V6Q20 0 40 6T80 6V12Z' fill='%23e2f6f6'/%3E%3C/svg%3E") left bottom / 80px 12px repeat-x,
linear-gradient(120deg, #1f8f9a, #2fa89c);
border-bottom: none;
padding-bottom: calc(var(--bs-modal-header-padding-y, 1rem) + 10px);
h2 {
color: #fff;
font-family: var(--g5-display);
font-weight: 800;
}
.btn-close {
filter: invert(1) grayscale(1) brightness(2);
opacity: 0.9;
}
}
.modal-footer {
border-top: 2px solid var(--g5-line);
}
.spin-steps span {
font-family: var(--g5-display);
font-weight: 800;
background: #f3ece6;
color: #908aa3;
border-radius: 999px;
padding: 2px 16px;
&.active {
background: linear-gradient(120deg, var(--g5-sunny), var(--g5-pipp));
color: #fff;
}
&.done {
background: var(--g5-hitch);
color: #fff;
}
}
.btn {
font-family: var(--g5-font);
font-weight: 800;
border-radius: 999px;
}
.btn-primary {
background: linear-gradient(120deg, var(--g5-sunny), var(--g5-pipp));
border: none;
color: #fff;
text-shadow: 0 1px 0 rgb(0 0 0 / 0.15);
box-shadow: 0 4px 0 #c43d82;
&:hover:not(:disabled) {
background: linear-gradient(120deg, var(--g5-sunny), var(--g5-pipp));
color: #fff;
filter: brightness(1.07) saturate(1.1);
}
&:active:not(:disabled) {
transform: translateY(3px);
box-shadow: 0 1px 0 #c43d82;
}
&:disabled {
background: #e3dde9;
color: #908aa3;
text-shadow: none;
box-shadow: 0 4px 0 #cbc4d6;
}
}
// Tlačítko roztočení — velká buclatá pilulka
.spin-button {
font-family: var(--g5-display);
font-size: 1.35rem;
padding: 8px 40px 4px;
box-shadow: 0 5px 0 #c43d82, 0 10px 20px -6px rgb(238 95 168 / 0.6);
}
.btn-outline-primary {
background: var(--g5-panel);
border: 2px solid var(--g5-pipp);
color: var(--g5-sunny-deep);
&:hover {
background: #ffeadb;
color: var(--g5-sunny-deep);
}
}
.btn-secondary {
background: var(--g5-hitch);
border: none;
color: #fff;
box-shadow: 0 4px 0 #1e7a70;
}
.spin-source {
background: #f3ece6;
> .btn-primary {
box-shadow: none !important;
}
> .btn-outline-primary {
background: transparent;
color: var(--g5-text);
&:hover {
background: var(--g5-panel);
}
}
}
.spin-result {
font-weight: 800;
strong {
color: var(--g5-sunny-deep);
}
}
.spin-options {
background: var(--g5-panel);
border: 1px solid var(--g5-line);
border-radius: 20px;
li:hover {
background: #fff1e5;
}
}
.spin-options-header {
border-bottom: 2px solid var(--g5-line);
color: var(--g5-pipp);
font-family: var(--g5-display);
}
.spin-empty {
border-color: var(--g5-line);
}
.spin-wheel-pointer {
border-top-color: var(--g5-pipp);
filter: drop-shadow(0 3px 2px rgb(196 61 130 / 0.45));
}
.spin-seg {
stroke: #fff;
stroke-width: 3;
}
.spin-seg-label {
font-family: var(--g5-display);
font-weight: 800;
}
}
// Tmavý režim — noční Bridlewood
[data-bs-theme="dark"] .spin-wheel-modal.spin-theme-mlpg5 {
--g5-sunny: #ffa560;
--g5-sunny-deep: #ff9a4d;
--g5-pipp: #ff7cbb;
--g5-hitch: #1f8f86;
--g5-line: #2c3650;
--g5-panel: #1a2233;
--g5-text: #f1eefa;
--wheel-rim: #0c3a42;
.modal-content {
background:
radial-gradient(circle at 12% 20%, rgb(79 209 194 / 0.14) 0 40px, transparent 110px),
radial-gradient(circle at 88% 70%, rgb(184 148 242 / 0.16) 0 50px, transparent 130px),
#0f1724;
}
.modal-header {
background:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='12'%3E%3Cpath d='M0 12V6Q20 0 40 6T80 6V12Z' fill='%230f1724'/%3E%3C/svg%3E") left bottom / 80px 12px repeat-x,
linear-gradient(120deg, #0c3a42, #12505a);
}
.spin-steps span:not(.active):not(.done) {
background: #222c40;
color: #8e89a6;
}
.btn-primary:disabled {
background: #2c3650;
color: #8e89a6;
box-shadow: 0 4px 0 #1f273a;
}
.btn-outline-primary:hover {
background: #3a2a26;
}
.btn-secondary {
box-shadow: 0 4px 0 #125a54;
}
.spin-source {
background: #222c40;
}
.spin-options li:hover {
background: #222c40;
}
.spin-seg {
stroke: #0f1724;
}
}
// Ikona kola se při najetí pootočí // Ikona kola se při najetí pootočí
.spin-wheel-icon { .spin-wheel-icon {
transition: transform 0.4s ease !important; transition: transform 0.4s ease !important;
+4 -3
View File
@@ -13,12 +13,13 @@ const AUTO_ORDER_CHOICE_KEY = 'auto_order_choice';
export type ThemePreference = 'system' | 'light' | 'dark'; export type ThemePreference = 'system' | 'light' | 'dark';
/** Vizuální styl aplikace (nezávislý na světlém/tmavém režimu). */ /** Vizuální styl aplikace (nezávislý na světlém/tmavém režimu). */
export type StyleTheme = 'simple' | 'umamusume' | 'mlp' | 'hellgirl' | 'kon' | 'bocchi' | 'evangelion' | 'yurucamp' | 'spyfamily' | 'apothecary' | 'frieren'; export type StyleTheme = 'simple' | 'umamusume' | 'mlp' | 'hellgirl' | 'kon' | 'bocchi' | 'evangelion' | 'yurucamp' | 'spyfamily' | 'apothecary' | 'frieren' | 'mlpg5';
export const STYLE_THEMES: { value: StyleTheme, label: string }[] = [ export const STYLE_THEMES: { value: StyleTheme, label: string }[] = [
{ value: 'simple', label: 'Jednoduchý' }, { value: 'simple', label: 'Jednoduchý' },
{ value: 'umamusume', label: 'Umamusume' }, { value: 'umamusume', label: 'Umamusume' },
{ value: 'mlp', label: 'My Little Pony' }, { value: 'mlp', label: 'My Little Pony (G4)' },
{ value: 'mlpg5', label: 'My Little Pony (G5)' },
{ value: 'hellgirl', label: 'Hell Girl' }, { value: 'hellgirl', label: 'Hell Girl' },
{ value: 'kon', label: 'K-ON!' }, { value: 'kon', label: 'K-ON!' },
{ value: 'bocchi', label: 'Bocchi the Rock!' }, { value: 'bocchi', label: 'Bocchi the Rock!' },
@@ -258,7 +259,7 @@ function useProvideSettings(): SettingsContextProps {
}, [autoOrderChoice]); }, [autoOrderChoice]);
// Aplikuje accent barvy při změně hue nebo přepnutí světlý/tmavý. // Aplikuje accent barvy při změně hue nebo přepnutí světlý/tmavý.
// Tematické styly (Umamusume, My Little Pony, Hell Girl, K-ON!, Bocchi the Rock!, Evangelion, Yuru Camp△, Spy × Family, Apothecary Diaries, Frieren) mají vlastní pevnou paletu, accent barvu proto uplatňuje jen jednoduchý styl. // Tematické styly (Umamusume, My Little Pony, Hell Girl, K-ON!, Bocchi the Rock!, Evangelion, Yuru Camp△, Spy × Family, Apothecary Diaries, Frieren, My Little Pony G5) mají vlastní pevnou paletu, accent barvu proto uplatňuje jen jednoduchý styl.
useEffect(() => { useEffect(() => {
localStorage.setItem(ACCENT_HUE_KEY, String(accentHue)); localStorage.setItem(ACCENT_HUE_KEY, String(accentHue));
if (styleTheme === 'simple') { if (styleTheme === 'simple') {
+2
View File
@@ -25,6 +25,8 @@ const ICONS: Record<StyleTheme, Record<GameId, string>> = {
apothecary: { drawing: '🖋️' }, apothecary: { drawing: '🖋️' },
// Hůl mágyně Frieren // Hůl mágyně Frieren
frieren: { drawing: '🪄' }, frieren: { drawing: '🪄' },
// Izzy Moonbow a její výtvory z vyřazených věcí
mlpg5: { drawing: '🦄' },
}; };
/** Styl z <html data-luncher-theme> — pro místa mimo React kontext nastavení. */ /** Styl z <html data-luncher-theme> — pro místa mimo React kontext nastavení. */
+539
View File
@@ -0,0 +1,539 @@
// ============================================
// STYL "MY LITTLE PONY (G5)" (volitelný, výchozí je jednoduchý styl)
// Inspirováno 5. generací — A New Generation / Make Your Mark / Tell Your Tale.
// Letní Maretime Bay: tyrkysový oceán šerifa Hitche, západ slunce v barvách
// Sunny Starscout a Pipp Petals, vlnky a barevné odlesky krystalů jednoty.
// Titulek dne jako příspěvek z Pippina kanálu i s lajky.
// Tmavý režim = noční Bridlewood se svítícími krystaly.
// Aktivní, pokud má <html> atribut data-luncher-theme="mlpg5".
// ============================================
$g5: 'html[data-luncher-theme="mlpg5"]';
// Vlnka — horní hrana „vody" zvedající se do prvku nad ní (barva jako parametr)
@function g5-wave-up($color) {
@return url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='12'%3E%3Cpath d='M0 12V6Q20 0 40 6T80 6V12Z' fill='#{$color}'/%3E%3C/svg%3E");
}
// Vlnka visící dolů pod prvkem (barva jako parametr)
@function g5-wave-down($color) {
@return url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='12'%3E%3Cpath d='M0 0H80V6Q60 12 40 6T0 6Z' fill='#{$color}'/%3E%3C/svg%3E");
}
// Barvy Mane 5 + Misty: Sunny, Pipp, Izzy, Zipp, Hitch
$g5-mane: linear-gradient(90deg, #f08a3c, #ee5fa8, #9b6fe0, #5a7bd8, #2fa89c);
#{$g5} {
--g5-font: 'Nunito', -apple-system, sans-serif;
--g5-display: 'Baloo 2', 'Nunito', sans-serif;
--g5-sunny: #f08a3c; // Sunny Starscout
--g5-sunny-deep: #c85a14;
--g5-pipp: #ee5fa8; // Pipp Petals
--g5-izzy: #9b6fe0; // Izzy Moonbow
--g5-zipp: #5a7bd8; // Zipp Storm
--g5-hitch: #2fa89c; // Hitch Trailblazer
--g5-ocean: #1f8f9a; // záliv Maretime Bay
--g5-ocean-deep: #16707a;
--g5-card: #ffffff;
--g5-line: #f3dccb;
--luncher-primary: var(--g5-sunny-deep);
--luncher-primary-hover: #a84a0e;
--luncher-primary-light: #ffeadb;
--luncher-bg: #fff8f1;
--luncher-bg-card: var(--g5-card);
--luncher-bg-hover: #fff1e5;
--luncher-text: #2b2440;
--luncher-text-secondary: #564d6e;
--luncher-text-muted: #908aa3;
--luncher-border: var(--g5-line);
--luncher-border-light: #f8eadf;
--luncher-navbar-bg: var(--g5-ocean);
--luncher-navbar-text: #ffffff;
--luncher-action-icon: var(--g5-sunny-deep);
--luncher-buyer-icon: var(--g5-hitch);
--luncher-success: #2f9a5a;
--luncher-warning: #e8a01c;
--luncher-danger: #e0395c;
--luncher-radius-sm: 12px;
--luncher-radius: 16px;
--luncher-radius-lg: 22px;
--luncher-radius-xl: 26px;
--luncher-shadow: 0 6px 18px -6px rgb(200 90 20 / 0.22);
--luncher-shadow-lg: 0 14px 32px -10px rgb(238 95 168 / 0.35);
&[data-bs-theme="dark"] {
// Noční Bridlewood — krystaly svítí v lese
--g5-sunny: #ffa560;
--g5-sunny-deep: #ff9a4d;
--g5-pipp: #ff7cbb;
--g5-izzy: #b894f2;
--g5-zipp: #8aa3f0;
--g5-hitch: #4fd1c2;
--g5-ocean: #12505a;
--g5-ocean-deep: #0c3a42;
--g5-card: #1a2233;
--g5-line: #2c3650;
--luncher-primary: #ffa560;
--luncher-primary-hover: #ffbb85;
--luncher-primary-light: #3a2a26;
--luncher-bg: #0f1724;
--luncher-bg-hover: #222c40;
--luncher-text: #f1eefa;
--luncher-text-secondary: #c9c3dc;
--luncher-text-muted: #8e89a6;
--luncher-border-light: #232c40;
--luncher-navbar-bg: var(--g5-ocean);
--luncher-action-icon: #ffa560;
--luncher-buyer-icon: #4fd1c2;
--luncher-success: #6fd99a;
--luncher-warning: #ffc04d;
--luncher-danger: #ff6f8e;
--luncher-shadow: 0 6px 18px -6px rgb(0 0 0 / 0.5);
--luncher-shadow-lg: 0 14px 32px -10px rgb(184 148 242 / 0.35);
}
body {
font-family: var(--g5-font);
background-color: var(--luncher-bg);
// Letní nebe nad zálivem a barevné odlesky krystalů jednoty
background-image:
radial-gradient(circle at 12% 22%, rgb(240 138 60 / 0.16) 0 60px, transparent 120px),
radial-gradient(circle at 88% 30%, rgb(238 95 168 / 0.14) 0 70px, transparent 140px),
radial-gradient(circle at 78% 80%, rgb(155 111 224 / 0.12) 0 60px, transparent 130px),
radial-gradient(circle at 18% 85%, rgb(47 168 156 / 0.12) 0 70px, transparent 140px),
linear-gradient(180deg, #d4f2f3 0, #fff1e6 340px, var(--luncher-bg) 600px);
background-attachment: fixed;
}
&[data-bs-theme="dark"] body {
background-image:
radial-gradient(circle at 12% 22%, rgb(79 209 194 / 0.16) 0 50px, transparent 120px),
radial-gradient(circle at 88% 30%, rgb(184 148 242 / 0.18) 0 60px, transparent 140px),
radial-gradient(circle at 78% 80%, rgb(255 124 187 / 0.12) 0 50px, transparent 130px),
radial-gradient(circle at 18% 85%, rgb(138 163 240 / 0.14) 0 60px, transparent 140px),
radial-gradient(1.2px 1.2px at 40px 60px, rgb(255 255 255 / 0.7), transparent) 0 0 / 200px 200px,
radial-gradient(1px 1px at 150px 130px, rgb(220 255 250 / 0.6), transparent) 0 0 / 200px 200px,
linear-gradient(180deg, #13283a 0, var(--luncher-bg) 420px);
}
.app-container {
background: transparent;
}
h1, h2, h3, h4, .navbar-brand {
font-family: var(--g5-display);
font-weight: 800;
}
.btn {
font-family: var(--g5-font);
}
// ---------- Navbar: tyrkysový záliv, pod ním vlnka ----------
.navbar {
position: relative;
z-index: 2;
background: var(--luncher-navbar-bg) !important;
// Vlnka visící z navbaru do stránky
&::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: -11px;
height: 12px;
background: g5-wave-down('%231f8f9a') left top / 80px 12px repeat-x;
pointer-events: none;
}
.navbar-brand {
letter-spacing: 0.01em;
// Sluníčko z cutie marku Sunny se pomalu otáčí
&::after {
content: '☀️';
display: inline-block;
margin-left: 6px;
margin-right: 0.6em;
font-size: 0.75em;
animation: g5-sun 12s linear infinite;
}
}
.dropdown-menu {
border: 1px solid var(--g5-line);
border-radius: var(--luncher-radius);
box-shadow: var(--luncher-shadow-lg);
}
}
&[data-bs-theme="dark"] .navbar::after {
background-image: g5-wave-down('%2312505a');
}
// ---------- Titulek dne: příspěvek z Pippina kanálu s duhovým rámečkem a lajky ----------
.day-navigator .title {
position: relative;
display: inline-block;
padding: 6px 30px 4px;
font-family: var(--g5-display);
font-weight: 800;
font-size: 1.7rem;
color: var(--luncher-text) !important;
border: 3px solid transparent;
border-radius: 999px;
background:
linear-gradient(var(--g5-card), var(--g5-card)) padding-box,
#{$g5-mane} border-box;
box-shadow: 0 8px 20px -8px rgb(238 95 168 / 0.5);
// Bublina s lajky od Pippsqueaks
&::after {
content: '♥ 1,2k';
position: absolute;
top: -12px;
right: -16px;
padding: 1px 8px;
font-family: var(--g5-font);
font-size: 0.7rem;
font-weight: 800;
color: #fff;
background: var(--g5-pipp);
border-radius: 999px;
box-shadow: 0 3px 8px rgb(238 95 168 / 0.45);
transform: rotate(8deg);
animation: g5-like 3s ease-in-out infinite;
}
&.text-muted {
color: var(--luncher-text-muted) !important;
background:
linear-gradient(var(--g5-card), var(--g5-card)) padding-box,
linear-gradient(90deg, #d8d2e2, #c6c0d2) border-box;
box-shadow: none;
&::after {
content: '♡';
background: #b8b2c6;
box-shadow: none;
animation: none;
}
}
}
// ---------- Karty podniků: hlavička jako západ slunce nad vlnami ----------
.restaurant-card {
border: none;
border-radius: var(--luncher-radius-lg);
box-shadow: var(--luncher-shadow);
overflow: hidden;
&:hover {
transform: translateY(-4px);
box-shadow: var(--luncher-shadow-lg);
}
.restaurant-header {
background:
g5-wave-up('%23ffffff') left bottom / 80px 12px repeat-x,
linear-gradient(120deg, var(--g5-sunny), var(--g5-pipp));
padding-bottom: 24px;
// Při najetí tyrkysový oceán
&:hover {
background:
g5-wave-up('%23ffffff') left bottom / 80px 12px repeat-x,
linear-gradient(120deg, var(--g5-hitch), var(--g5-zipp));
}
small {
color: rgb(255 255 255 / 0.9);
}
}
}
&[data-bs-theme="dark"] .restaurant-card .restaurant-header {
background:
g5-wave-up('%231a2233') left bottom / 80px 12px repeat-x,
linear-gradient(120deg, #c4602a, #b83f80);
&:hover {
background:
g5-wave-up('%231a2233') left bottom / 80px 12px repeat-x,
linear-gradient(120deg, #1f8f86, #4a5fb8);
}
}
.food-table tbody tr:hover > td {
background: #fff1e5;
}
&[data-bs-theme="dark"] .food-table tbody tr:hover > td {
background: #222c40;
}
.food-price {
color: var(--g5-sunny-deep);
font-weight: 800;
}
// ---------- Panely a tabulka voleb ----------
.choice-section,
.choices-table,
.pizza-section,
.no-votes {
border: 1px solid var(--g5-line);
border-radius: var(--luncher-radius-lg);
box-shadow: var(--luncher-shadow);
}
.choices-table {
border-collapse: separate;
border-spacing: 0;
}
// Sloupec s podnikem: barvy západu slunce
.choices-table > tbody > tr > td:first-child {
background: linear-gradient(135deg, #ffe7d4, #ffe0ee);
color: #8a3a12;
font-family: var(--g5-display);
font-weight: 800;
border-right: 1px solid var(--g5-line);
}
&[data-bs-theme="dark"] .choices-table > tbody > tr > td:first-child {
background: linear-gradient(135deg, #3a2a26, #3a2238);
color: var(--g5-sunny);
}
// Bootstrap karty (skupinové objednávky apod.) a seznamy
.card {
border: 1px solid var(--g5-line);
border-radius: var(--luncher-radius-lg);
box-shadow: var(--luncher-shadow);
overflow: hidden;
background: var(--luncher-bg-card);
// Rozbalovací výběr z nabídky nesmí karta oříznout
&:has(.select-search-container) {
overflow: visible;
}
> .card-header {
background: var(--luncher-primary-light);
border-bottom: 3px solid transparent;
border-image: #{$g5-mane} 1;
font-weight: 800;
}
.table {
--bs-table-bg: transparent;
margin-bottom: 0;
> :last-child > tr:last-child > * {
border-bottom-width: 0;
}
}
}
.list-group {
border-radius: var(--luncher-radius);
overflow: hidden;
border: 1px solid var(--g5-line);
> .list-group-item {
border-left: none;
border-right: none;
&:first-child {
border-top: none;
}
&:last-child {
border-bottom: none;
}
}
}
.form-select, .form-control,
input[type="text"], input[type="email"], input[type="password"] {
border-radius: var(--luncher-radius-sm);
border: 2px solid var(--g5-line);
&:focus {
border-color: var(--g5-pipp);
box-shadow: 0 0 0 4px rgb(238 95 168 / 0.2);
}
}
// ---------- Tlačítka: buclatá jako z animáku, s 3D spodkem ----------
.btn {
border-radius: 999px;
font-weight: 800;
transition: transform 0.12s ease, box-shadow 0.2s ease, background 0.2s ease, filter 0.2s ease;
}
.btn-primary {
background: linear-gradient(120deg, var(--g5-sunny), var(--g5-pipp));
border: none;
color: #fff;
text-shadow: 0 1px 0 rgb(0 0 0 / 0.15);
box-shadow: 0 4px 0 #c43d82, 0 8px 16px -6px rgb(238 95 168 / 0.6);
&:hover {
background: linear-gradient(120deg, var(--g5-sunny), var(--g5-pipp));
color: #fff;
filter: brightness(1.07) saturate(1.1);
transform: translateY(-2px);
box-shadow: 0 6px 0 #c43d82, 0 12px 20px -6px rgb(238 95 168 / 0.6);
}
&:active {
transform: translateY(3px) !important;
box-shadow: 0 1px 0 #c43d82 !important;
}
&:disabled {
background: #e3dde9;
color: #908aa3;
text-shadow: none;
box-shadow: 0 4px 0 #cbc4d6;
}
}
&[data-bs-theme="dark"] .btn-primary:disabled {
background: #2c3650;
color: #8e89a6;
box-shadow: 0 4px 0 #1f273a;
}
.btn-outline-primary {
background: var(--luncher-bg-card);
border: 2px solid var(--g5-pipp);
color: var(--luncher-primary);
&:hover {
background: var(--luncher-primary-light);
border-color: var(--g5-pipp);
color: var(--luncher-primary);
}
}
.btn-secondary {
background: var(--g5-hitch);
border: none;
color: #fff;
box-shadow: 0 4px 0 #1e7a70;
&:hover {
background: #36baad;
color: #fff;
}
}
&[data-bs-theme="dark"] .btn-secondary {
background: #1f8f86;
box-shadow: 0 4px 0 #125a54;
}
.btn-danger {
background: #e0395c;
border: none;
box-shadow: 0 4px 0 #a8203f;
}
.action-icon:hover {
background: var(--luncher-primary-light);
color: var(--luncher-primary);
}
// ---------- Modaly: záliv s vlnkou ----------
.modal-content {
border: none;
border-radius: var(--luncher-radius-xl);
box-shadow: 0 20px 50px -12px rgb(31 143 154 / 0.45);
overflow: hidden;
}
.modal-header {
background:
g5-wave-up('%23ffffff') left bottom / 80px 12px repeat-x,
linear-gradient(120deg, var(--g5-ocean), var(--g5-hitch));
border-bottom: none;
padding-bottom: calc(var(--bs-modal-header-padding-y, 1rem) + 10px);
.modal-title, .modal-title h2 {
color: #fff;
font-family: var(--g5-display);
font-weight: 800;
}
.btn-close {
filter: invert(1) grayscale(1) brightness(2);
}
}
&[data-bs-theme="dark"] .modal-header {
background:
g5-wave-up('%231a2233') left bottom / 80px 12px repeat-x,
linear-gradient(120deg, #0c3a42, #12505a);
}
// ---------- Footer: oceán, do kterého se zvedá vlnka ----------
.footer {
position: relative;
border-top: none;
&::before {
content: '';
position: absolute;
left: 0;
right: 0;
top: -11px;
height: 12px;
background: g5-wave-up('%231f8f9a') left bottom / 80px 12px repeat-x;
pointer-events: none;
}
a {
color: #ffe0b8;
}
}
&[data-bs-theme="dark"] .footer::before {
background-image: g5-wave-up('%2312505a');
}
}
// Sluníčko Sunny se pomalu otáčí
@keyframes g5-sun {
to { transform: rotate(360deg); }
}
// Lajky občas „pípnou"
@keyframes g5-like {
0%, 80%, 100% { transform: rotate(8deg) scale(1); }
86% { transform: rotate(8deg) scale(1.2); }
92% { transform: rotate(8deg) scale(0.95); }
}
@media (max-width: 575.98px) {
#{$g5} .day-navigator .title {
font-size: 1.35rem;
padding: 4px 20px 2px;
&::after {
right: -6px;
}
}
}
@media (prefers-reduced-motion: reduce) {
#{$g5} .navbar .navbar-brand::after,
#{$g5} .day-navigator .title::after {
animation: none;
}
}