feat: nový vzhled Chiikawa
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 27s
CI / Build server (push) Successful in 29s
CI / Build client (push) Successful in 42s
CI / Playwright E2E tests (push) Successful in 1m58s
CI / Build and push Docker image (push) Successful in 46s
CI / Notify (push) Successful in 2s

This commit is contained in:
mates committed 2026-10-27 11:21:44 +01:00
1 parent a2dbd4c04b
commit 882a2f38a7
10 files changed
+1051 -6

No files matched your search

+2 -2
View File
@@ -11,7 +11,7 @@
<link rel="manifest" href="/manifest.json" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<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&family=Baloo+2:wght@600;800&family=Metal+Mania&family=Klee+One:wght@600&family=Exo+2:wght@700;800&family=Almendra:wght@700&family=Alegreya:wght@500;700;800&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&family=Metal+Mania&family=Klee+One:wght@600&family=Exo+2:wght@700;800&family=Almendra:wght@700&family=Alegreya:wght@500;700;800&family=Hachi+Maru+Pop&display=swap" rel="stylesheet" />
<title>Luncher</title>
<script>
(function() {
@@ -28,7 +28,7 @@
}
document.documentElement.setAttribute('data-bs-theme', theme);
var style = localStorage.getItem('style_theme');
var styles = ['umamusume', 'mlp', 'hellgirl', 'kon', 'bocchi', 'evangelion', 'yurucamp', 'spyfamily', 'apothecary', 'frieren', 'mlpg5', 'aggretsuko', 'sumikko', 'gudetama', 'miku', 'dungeonmeshi'];
var styles = ['umamusume', 'mlp', 'hellgirl', 'kon', 'bocchi', 'evangelion', 'yurucamp', 'spyfamily', 'apothecary', 'frieren', 'mlpg5', 'aggretsuko', 'sumikko', 'gudetama', 'miku', 'dungeonmeshi', 'chiikawa'];
document.documentElement.setAttribute('data-luncher-theme', styles.indexOf(style) >= 0 ? style : 'simple');
} catch (e) {
// Fallback pokud localStorage není dostupný
+1
View File
@@ -26,6 +26,7 @@ import './themes/sumikko.scss';
import './themes/gudetama.scss';
import './themes/miku.scss';
import './themes/dungeonmeshi.scss';
import './themes/chiikawa.scss';
import { faCircleCheck, faNoteSticky, faTrashCan, faComment } from '@fortawesome/free-regular-svg-icons';
import { useSettings } from './context/settings';
import Footer from './components/Footer';
+11
View File
@@ -197,6 +197,17 @@
box-shadow: 0 0 0 2px #6b4226, 0 2px 4px rgb(59 42 26 / 0.35);
}
// Styl Chiikawa: emoji na bílé mochi nálepce s hnědým obrysem a odsazeným stínem
.eater-badge-chiikawa {
display: inline-grid;
place-items: center;
font-size: 15px;
line-height: 1;
border-color: #4a3f3a;
background: radial-gradient(circle at 40% 35%, #ffffff, #fff1f4);
box-shadow: 0 2px 0 #4a3f3a;
}
// Odznak a jméno drží na jednom řádku (úzký sloupec „Člen" na stránce objednávek)
.user-info:has(.eater-badge) {
display: inline-flex;
+8 -1
View File
@@ -103,6 +103,12 @@ const DM_LEVELS = [
{ minItems: 2, emoji: '🍄', title: 'Úroveň Senshiho — maso, zelenina a houby, vyvážená strava i v podzemí' },
];
/** Úrovně ve stylu Chiikawa — emoji na mochi nálepce s obrysem a ruměncem. Seřazeno od nejvyšší úrovně. */
const CK_LEVELS = [
{ minItems: 3, emoji: '🍜', title: 'Úroveň Usagi — Ura! Yaha! Ještě miska rámenu navrch!' },
{ minItems: 2, emoji: '🍮', title: 'Úroveň Chiikawy — po pletí plevele si zasloužíš pudink za odměnu' },
];
/** Úrovně podle stylu pro odznaky s emoji. */
const EMOJI_LEVELS: Partial<Record<StyleTheme, typeof MLP_LEVELS>> = {
mlp: MLP_LEVELS,
@@ -120,6 +126,7 @@ const EMOJI_LEVELS: Partial<Record<StyleTheme, typeof MLP_LEVELS>> = {
gudetama: GT_LEVELS,
miku: MIKU_LEVELS,
dungeonmeshi: DM_LEVELS,
chiikawa: CK_LEVELS,
};
type Props = {
@@ -127,7 +134,7 @@ type Props = {
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, Frieren, My Little Pony G5, Aggretsuko, Sumikko Gurashi, Gudetama, Hatsune Miku a Dungeon Meshi. */
/** 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, My Little Pony G5, Aggretsuko, Sumikko Gurashi, Gudetama, Hatsune Miku, Dungeon Meshi a Chiikawa. */
export default function EaterBadge({ items }: Readonly<Props>) {
const styleTheme = useSettings()?.styleTheme;
const isUma = styleTheme === 'umamusume';
+83
View File
@@ -1362,3 +1362,86 @@
}
}
}
// ============================================
// Konfety ve stylu Chiikawa
// ============================================
.confetti-ck {
filter: drop-shadow(0 2px 0 rgb(74 63 58 / 0.5));
}
// Štítky jako kulaté nálepky s hnědým obrysem a odsazeným stínem
.confetti-ck-badge {
padding: 4px 16px 5px;
border-radius: 999px;
border: 2px solid #4a3f3a;
box-shadow: 0 3px 0 #4a3f3a;
color: #fff;
font-family: 'Hachi Maru Pop', 'Zen Maru Gothic', sans-serif;
font-size: 1.1rem;
white-space: nowrap;
text-shadow: 0 1px 0 rgb(74 63 58 / 0.4);
}
// Nápis „✨ ちいかわ ✨" — velké mochi s ruměncem, které radostně poskakuje
.confetti-ck-banner {
position: absolute;
top: 38%;
left: 50%;
transform: translate(-50%, -50%);
padding: 18px 64px 20px;
font-family: 'Hachi Maru Pop', 'Zen Maru Gothic', sans-serif;
font-size: clamp(2rem, 7vw, 4.2rem);
white-space: nowrap;
color: #4a3f3a;
background:
radial-gradient(ellipse 22px 12px at 34px 64%, #f7a8b8 98%, transparent),
radial-gradient(ellipse 22px 12px at calc(100% - 34px) 64%, #f7a8b8 98%, transparent),
#ffffff;
border: 4px solid #4a3f3a;
border-radius: 999px;
box-shadow: 0 8px 0 #4a3f3a, 0 18px 40px rgb(247 168 184 / 0.6);
animation: ck-banner 2.6s ease both;
}
@keyframes ck-banner {
0% { transform: translate(-50%, -50%) scale(0.3); opacity: 0; }
12% { transform: translate(-50%, -50%) scale(1.12, 0.9); opacity: 1; }
20% { transform: translate(-50%, -50%) scale(0.95, 1.06); }
// Radostné poskakování
28%, 48%, 68% { transform: translate(-50%, -50%) scale(1); }
38%, 58% { transform: translate(-50%, -56%) scale(1.02, 0.98); }
80% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
100% { transform: translate(-50%, -50%) scale(0.9); opacity: 0; }
}
// Kamarád s hláškou — kulatá mochi nálepka a bublina s obrysem
.confetti-chiikawa-voice {
.confetti-mlp-avatar {
border: 3px solid #4a3f3a;
background: radial-gradient(circle at 40% 35%, #ffffff, #fff1f4);
box-shadow: 0 4px 0 #4a3f3a, 0 0 0 6px var(--mlp-speaker, #f08aa8);
}
.confetti-mlp-bubble {
background: #ffffff;
color: #4a3f3a;
border: 2.5px solid #4a3f3a;
border-radius: 22px;
box-shadow: 0 4px 0 #4a3f3a;
font-family: 'Zen Maru Gothic', sans-serif;
p {
font-weight: 700;
}
small {
font-family: 'Hachi Maru Pop', 'Zen Maru Gothic', sans-serif;
color: var(--mlp-speaker, #f08aa8);
}
&::before {
border-right-color: #4a3f3a;
}
}
}
+16 -1
View File
@@ -240,6 +240,20 @@ const DM_SPEAKERS = [
{ name: 'Falin', emoji: '🌼', color: '#c9a227', quote: 'To zní dobře! Hlavně ať jíme všichni spolu.' },
{ name: 'Kabru', emoji: '🗡️', color: '#3b6e4f', quote: 'Zajímavá volba. Kdo z vás to vybíral? Jen ať vím, s kým si promluvit.' },
];
const CK_EMOJI = ['🍮', '🍜', '🍙', '🌱', '🌸', '💦', '✨', '⭐', '🍡', '🥟', '🐰', '🐱', '🎸', '🌿', '🍓', '🎀'];
// Hlášky z Chiikawy (Hachiwarovo „Nantoka nare!", Usagiho výkřiky, Chiikawino „Waa…", pletí plevele...)
const CK_BADGES = ['ちいかわ', 'Nantoka nare!', 'Yaha!', 'Ura!', 'Pururu…', 'Waa…', 'Pletí plevele', 'Zkouška z pletí', 'Odměna!', 'Haa~', 'Pudink!'];
const CK_BADGE_COLORS = ['#f08aa8', '#7aa3d6', '#e0b93a', '#8cc97a', '#a98fd6', '#f2a36b'];
/** Chiikawa a kamarádi s „hláškou" po finálním výsledku. */
const CK_SPEAKERS = [
{ name: 'Chiikawa', emoji: '🍮', color: '#f08aa8', quote: 'Waa… waa! ✨ (radostně poskakuje a trochu se dojme)' },
{ name: 'Hachiware', emoji: '🎸', color: '#7aa3d6', quote: 'Paráda! A kdyby to nevyšlo… nějak to dopadne! Nantoka nare!' },
{ name: 'Usagi', emoji: '🐰', color: '#e0b93a', quote: 'Ura! Yaha! Hu-n… Pururururu!' },
{ name: 'Momonga', emoji: '🌰', color: '#a98fd6', quote: 'A kde je MOJE porce? Pochvalte mě, jsem přece roztomilý!' },
{ name: 'Kurimanju', emoji: '🍺', color: '#b5835a', quote: 'Haa~ K tomu by se hodilo jedno orosené… a něco malého na zub.' },
{ name: 'Rakko', emoji: '🦦', color: '#8a7a6a', quote: 'Dobrá volba. Po obědě ale zpátky do tréninku.' },
{ name: 'Shisa', emoji: '🦁', color: '#e6a23c', quote: 'Hlavně se pořádně najíst! A pak se zase snažit v práci!' },
];
/** Styly, jejichž konfety tvoří emoji a samolepky s hláškami (a po finále promluví postava s emoji). */
const EMOJI_CONFETTI: Partial<Record<WheelTheme, {
prefix: string, badges: string[], colors: string[], emoji: string[],
@@ -261,6 +275,7 @@ const EMOJI_CONFETTI: Partial<Record<WheelTheme, {
gudetama: { prefix: 'confetti-gt', badges: GT_BADGES, colors: GT_BADGE_COLORS, emoji: GT_EMOJI, speakers: GT_SPEAKERS, banner: '🍳 ぐでたま… 🥓' },
miku: { prefix: 'confetti-miku', badges: MIKU_BADGES, colors: MIKU_BADGE_COLORS, emoji: MIKU_EMOJI, speakers: MIKU_SPEAKERS, banner: '♪ 初音ミク ♪' },
dungeonmeshi: { prefix: 'confetti-dm', badges: DM_BADGES, colors: DM_BADGE_COLORS, emoji: DM_EMOJI, speakers: DM_SPEAKERS, banner: '🍲 ダンジョン飯 🔥' },
chiikawa: { prefix: 'confetti-ck', badges: CK_BADGES, colors: CK_BADGE_COLORS, emoji: CK_EMOJI, speakers: CK_SPEAKERS, banner: '✨ ちいかわ ✨' },
};
const DURATION_MS = 6400;
@@ -414,7 +429,7 @@ export default function WheelConfetti({ theme, burstKey, big = false }: Readonly
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [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, poníci z Maretime Bay, kancelář Retsuko, postavičky z koutků, Gudetama, Vocaloidi, družina z Dungeon Meshi...)
// 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, kancelář Retsuko, postavičky z koutků, Gudetama, Vocaloidi, družina z Dungeon Meshi, Chiikawa a kamarádi...)
const emojiConfetti = EMOJI_CONFETTI[theme];
const emojiSpeaker = useMemo(
() => big && emojiConfetti ? pick(emojiConfetti.speakers) : undefined,
@@ -4433,6 +4433,283 @@
}
}
// ============================================
// STYL CHIIKAWA kola štěstí (dle stylu zvoleného v nastavení).
// Pastelové výseče v barvách postav (Chiikawa, Hachiware, Usagi, tráva,
// Momonga, Shisa) s hnědými popisky, silný obrys jako v manze
// a střed jako bílé mochi.
// ============================================
.spin-wheel-modal.spin-theme-chiikawa {
--ck-font: 'Zen Maru Gothic', 'Nunito', sans-serif;
--ck-display: 'Hachi Maru Pop', 'Zen Maru Gothic', sans-serif;
--ck-ink: #4a3f3a;
--ck-line: #4a3f3a;
--ck-pink: #f7a8b8;
--ck-pink-deep: #c94f74;
--ck-white-pink: #fff1f4;
--ck-panel: #ffffff;
--ck-text: #4a3f3a;
--wheel-c0: #f7b6c6;
--wheel-c1: #a9c8ec;
--wheel-c2: #f7e08a;
--wheel-c3: #bfe3a8;
--wheel-c4: #d4c4ee;
--wheel-c5: #f8c9a0;
--wheel-rim: #4a3f3a;
--wheel-hub: #ffffff;
--wheel-label: #4a3f3a;
font-family: var(--ck-font);
.modal-content {
border: 2.5px solid var(--ck-line);
border-radius: 26px;
overflow: hidden;
background:
radial-gradient(circle at 10% 0%, rgb(247 168 184 / 0.25), transparent 45%),
radial-gradient(circle at 90% 100%, rgb(191 227 168 / 0.35), transparent 45%),
#fff8f2;
box-shadow: 0 6px 0 var(--ck-line);
color: var(--ck-text);
}
.modal-header {
background: #ffe3ea;
border-bottom: 2.5px solid var(--ck-line);
h2 {
color: var(--ck-ink);
font-family: var(--ck-display);
font-weight: 400;
}
}
.modal-footer {
border-top: 2px dashed var(--ck-pink);
}
.spin-steps span {
font-family: var(--ck-display);
background: #f6efeb;
color: #a3938c;
border: 2px solid #d9cbc4;
border-radius: 999px;
padding: 2px 16px;
&.active {
background: var(--ck-pink-deep);
color: #fff;
border-color: var(--ck-line);
box-shadow: 0 2px 0 var(--ck-line);
}
&.done {
background: #a9c8ec;
color: var(--ck-ink);
border-color: var(--ck-line);
}
}
.btn {
font-family: var(--ck-font);
font-weight: 900;
border-radius: 999px;
transition: transform 0.18s cubic-bezier(.3, 1.6, .5, 1), box-shadow 0.18s ease;
}
.btn-primary {
background: var(--ck-pink-deep);
border: 2px solid var(--ck-line);
color: #fff;
box-shadow: 0 3px 0 var(--ck-line);
&:hover:not(:disabled) {
background: #d6608a;
color: #fff;
transform: translateY(-2px);
box-shadow: 0 5px 0 var(--ck-line);
}
&:disabled {
background: #f1e6e2;
border-color: #c9bab3;
color: #a3938c;
box-shadow: none;
}
}
// Tlačítko roztočení — nálepka s jiskřičkou
.spin-button {
font-family: var(--ck-display);
font-weight: 400;
font-size: 1.25rem;
padding: 10px 38px;
&::before {
content: '✦ ';
color: #f7e08a;
}
}
.btn-outline-primary {
background: var(--ck-panel);
border: 2px solid var(--ck-line);
color: var(--ck-pink-deep);
box-shadow: 0 3px 0 var(--ck-line);
&:hover {
background: var(--ck-white-pink);
color: var(--ck-pink-deep);
}
}
.btn-secondary {
background: #7aa3d6;
border: 2px solid var(--ck-line);
color: #fff;
box-shadow: 0 3px 0 var(--ck-line);
}
.spin-source {
background: #f6efeb;
border: 2px solid var(--ck-line);
border-radius: 999px;
> .btn {
border-radius: 999px !important;
box-shadow: none;
border-color: transparent;
}
> .btn-outline-primary {
background: transparent;
color: var(--ck-text);
&:hover {
background: var(--ck-panel);
}
}
}
.spin-result {
font-weight: 900;
strong {
color: var(--ck-pink-deep);
}
}
.spin-options {
background: var(--ck-panel);
border: 2.5px solid var(--ck-line);
border-radius: 20px;
box-shadow: 0 3px 0 var(--ck-line);
li:hover {
background: var(--ck-white-pink);
}
}
.spin-options-header {
border-bottom: 2px dashed var(--ck-pink);
color: var(--ck-pink-deep);
font-family: var(--ck-display);
}
.spin-empty {
border-color: var(--ck-pink);
}
.spin-wheel-pointer {
border-top-color: var(--ck-pink-deep);
filter: drop-shadow(0 2px 0 var(--ck-line));
}
.spin-wheel-hub {
stroke: var(--ck-line);
stroke-width: 3;
}
.spin-seg {
stroke: var(--ck-line);
stroke-width: 2.5;
}
.spin-seg-label {
font-family: var(--ck-display);
font-weight: 400;
stroke: rgb(255 255 255 / 0.7);
stroke-width: 3px;
}
}
// Tmavý režim — hvězdná noc
[data-bs-theme="dark"] .spin-wheel-modal.spin-theme-chiikawa {
--ck-line: #12152b;
--ck-pink-deep: #ff9fb8;
--ck-white-pink: #3d2a42;
--ck-panel: #262b4d;
--ck-text: #f4eef6;
--ck-ink: #f4eef6;
--wheel-rim: #12152b;
--wheel-hub: #3a4170;
.modal-content {
border-color: #12152b;
background:
radial-gradient(circle at 92% 6%, #fff6c8 0 14px, rgb(255 246 200 / 0.2) 16px, transparent 50px),
linear-gradient(180deg, #1b1f3a, #232a52);
box-shadow: 0 6px 0 #12152b, 0 0 30px rgb(255 159 184 / 0.15);
}
.modal-header {
background: #2a2f55;
}
.spin-steps span:not(.active):not(.done) {
background: #2f355c;
color: #8e8aab;
border-color: #3a4170;
}
.spin-steps span.active {
color: #2a1f33;
}
.btn-primary {
color: #2a1f33;
&:hover:not(:disabled) {
background: #ffbfcf;
color: #2a1f33;
}
&:disabled {
background: #2f355c;
border-color: #12152b;
color: #8e8aab;
}
}
.btn-secondary {
background: #9cc3f0;
color: #1b1f3a;
}
.spin-source {
background: #2f355c;
}
.spin-options li:hover {
background: #2f355c;
}
// Popisky výsečí zůstávají tmavé — pastelové výseče jsou světlé i v noci
.spin-seg-label {
fill: #4a3f3a;
}
}
// Ikona kola se při najetí pootočí
.spin-wheel-icon {
transition: transform 0.4s ease !important;
+3 -2
View File
@@ -13,7 +13,7 @@ const AUTO_ORDER_CHOICE_KEY = 'auto_order_choice';
export type ThemePreference = 'system' | 'light' | 'dark';
/** 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' | 'mlpg5' | 'aggretsuko' | 'sumikko' | 'gudetama' | 'miku' | 'dungeonmeshi';
export type StyleTheme = 'simple' | 'umamusume' | 'mlp' | 'hellgirl' | 'kon' | 'bocchi' | 'evangelion' | 'yurucamp' | 'spyfamily' | 'apothecary' | 'frieren' | 'mlpg5' | 'aggretsuko' | 'sumikko' | 'gudetama' | 'miku' | 'dungeonmeshi' | 'chiikawa';
export const STYLE_THEMES: { value: StyleTheme, label: string }[] = [
{ value: 'simple', label: 'Jednoduchý' },
@@ -33,6 +33,7 @@ export const STYLE_THEMES: { value: StyleTheme, label: string }[] = [
{ value: 'gudetama', label: 'Gudetama' },
{ value: 'miku', label: 'Hatsune Miku' },
{ value: 'dungeonmeshi', label: 'Dungeon Meshi' },
{ value: 'chiikawa', label: 'Chiikawa' },
];
export type SettingsContextProps = {
@@ -264,7 +265,7 @@ function useProvideSettings(): SettingsContextProps {
}, [autoOrderChoice]);
// 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, My Little Pony G5, Aggretsuko, Sumikko Gurashi, Gudetama, Hatsune Miku, Dungeon Meshi) 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, Aggretsuko, Sumikko Gurashi, Gudetama, Hatsune Miku, Dungeon Meshi, Chiikawa) mají vlastní pevnou paletu, accent barvu proto uplatňuje jen jednoduchý styl.
useEffect(() => {
localStorage.setItem(ACCENT_HUE_KEY, String(accentHue));
if (styleTheme === 'simple') {
+2
View File
@@ -37,6 +37,8 @@ const ICONS: Record<StyleTheme, Record<GameId, string>> = {
miku: { drawing: '🎹' },
// Laiosův bestiář — náčrty příšer (a jak je uvařit)
dungeonmeshi: { drawing: '📖' },
// Výtvarná chvilka po pletí plevele — kreslí se klacíkem i pastelkou
chiikawa: { drawing: '🌱' },
};
/** Styl z <html data-luncher-theme> — pro místa mimo React kontext nastavení. */
+648
View File
@@ -0,0 +1,648 @@
// ============================================
// STYL "CHIIKAWA" (volitelný, výchozí je jednoduchý styl)
// Inspirováno mangou a anime Chiikawa (ちいかわ) od Nagana.
// Světlý režim = pastelová louka, kde se pleje plevel: krémové nebe,
// dole trsy trávy, silné hnědošedé obrysy jako v manze a tvary jako mochi.
// Karty podniků se střídají v barvách Chiikawy, Hachiware a Usagi, každá
// s obličejem postavy; zpoza titulku dne vykukuje Chiikawa.
// Tmavý režim = hvězdná noc s měsícem, pastelové akcenty září.
// Aktivní, pokud má <html> atribut data-luncher-theme="chiikawa".
// ============================================
$ck: 'html[data-luncher-theme="chiikawa"]';
// Obličeje postav (bílá Chiikawa s kulatýma ušima, Hachiware s modrou „čepičkou", žlutá Usagi s dlouhýma ušima),
// trsy trávy s kytičkou a hvězdná obloha
$ck-chiikawa: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cg stroke='%234a3f3a' stroke-width='1.8' stroke-linejoin='round'%3E%3Ccircle cx='10' cy='11' r='5' fill='%23fff'/%3E%3Ccircle cx='30' cy='11' r='5' fill='%23fff'/%3E%3Cellipse cx='20' cy='24' rx='16' ry='13' fill='%23fff'/%3E%3Cg stroke='none'%3E%3Ccircle cx='14' cy='23' r='2' fill='%233a302c'/%3E%3Ccircle cx='26' cy='23' r='2' fill='%233a302c'/%3E%3Cellipse cx='10' cy='28' rx='3.2' ry='1.8' fill='%23f7a8b8'/%3E%3Cellipse cx='30' cy='28' rx='3.2' ry='1.8' fill='%23f7a8b8'/%3E%3C/g%3E%3Cpath d='M18 27q2 2 4 0' fill='none' stroke-width='1.4'/%3E%3C/g%3E%3C/svg%3E");
$ck-hachiware: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cg stroke='%234a3f3a' stroke-width='1.8' stroke-linejoin='round'%3E%3Cpath d='M6 17L9 4L17 11Z' fill='%237aa3d6'/%3E%3Cpath d='M34 17L31 4L23 11Z' fill='%237aa3d6'/%3E%3Cellipse cx='20' cy='24' rx='16' ry='13' fill='%23fff'/%3E%3Cpath d='M5.5 20Q8 11 20 11Q32 11 34.5 20L22.5 20L20 25L17.5 20Z' fill='%237aa3d6' stroke='none'/%3E%3Cg stroke='none'%3E%3Ccircle cx='14' cy='23' r='2' fill='%233a302c'/%3E%3Ccircle cx='26' cy='23' r='2' fill='%233a302c'/%3E%3Cellipse cx='10' cy='28' rx='3.2' ry='1.8' fill='%23f7a8b8'/%3E%3Cellipse cx='30' cy='28' rx='3.2' ry='1.8' fill='%23f7a8b8'/%3E%3C/g%3E%3Cpath d='M17 28q1.5 1.6 3 0q1.5 1.6 3 0' fill='none' stroke-width='1.3'/%3E%3C/g%3E%3C/svg%3E");
$ck-usagi: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cg stroke='%234a3f3a' stroke-width='1.8' stroke-linejoin='round'%3E%3Cellipse cx='14' cy='9' rx='3.6' ry='8.5' fill='%23f7e08a'/%3E%3Cellipse cx='26' cy='9' rx='3.6' ry='8.5' fill='%23f7e08a'/%3E%3Cellipse cx='20' cy='25' rx='15' ry='12' fill='%23f7e08a'/%3E%3Cg stroke='none'%3E%3Ccircle cx='14' cy='23' r='2' fill='%233a302c'/%3E%3Ccircle cx='26' cy='23' r='2' fill='%233a302c'/%3E%3Cellipse cx='10' cy='28' rx='3.2' ry='1.8' fill='%23f7a8b8'/%3E%3Cellipse cx='30' cy='28' rx='3.2' ry='1.8' fill='%23f7a8b8'/%3E%3C/g%3E%3Cellipse cx='20' cy='29' rx='2' ry='2.4' fill='%23c2455f' stroke-width='1.2'/%3E%3C/g%3E%3C/svg%3E");
$ck-grass: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='28'%3E%3Cpath d='M0 28Q5 14 8 28Q12 8 16 28Q19 16 22 28ZM50 28Q53 18 56 28Q60 6 64 28Q68 14 71 28Q74 20 76 28Z' fill='%239fd18b'/%3E%3Ccircle cx='38' cy='22' r='3' fill='%23fff' stroke='%23f7a8b8' stroke-width='2'/%3E%3C/svg%3E");
$ck-stars: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cg fill='%23fff6c8'%3E%3Ccircle cx='20' cy='30' r='1.4'/%3E%3Ccircle cx='90' cy='14' r='1'/%3E%3Ccircle cx='130' cy='70' r='1.6'/%3E%3Ccircle cx='60' cy='100' r='1'/%3E%3Ccircle cx='110' cy='140' r='1.2'/%3E%3Ccircle cx='30' cy='140' r='0.9'/%3E%3C/g%3E%3Cpath d='M75 60l2 5 5 2-5 2-2 5-2-5-5-2 5-2z' fill='%23ffe58f'/%3E%3C/svg%3E");
#{$ck} {
--ck-font: 'Zen Maru Gothic', 'Nunito', sans-serif;
--ck-display: 'Hachi Maru Pop', 'Zen Maru Gothic', sans-serif;
--ck-ink: #4a3f3a; // obrys jako kresba v manze
--ck-pink: #f7a8b8; // ruměnec Chiikawy
--ck-pink-deep: #c94f74;
--ck-blue: #7aa3d6; // Hachiware
--ck-blue-light: #e3eefb;
--ck-yellow: #f7e08a; // Usagi
--ck-yellow-light: #fff6d2;
--ck-white-pink: #fff1f4; // Chiikawa
--ck-green: #9fd18b; // tráva na pletí
--ck-line: #4a3f3a;
--ck-soft-line: #eadcd6;
--luncher-primary: var(--ck-pink-deep);
--luncher-primary-hover: #b03f62;
--luncher-primary-light: #ffe6ec;
--luncher-bg: #fff8f2;
--luncher-bg-card: #ffffff;
--luncher-bg-hover: #fff1f4;
--luncher-text: var(--ck-ink);
--luncher-text-secondary: #6b5d57;
--luncher-text-muted: #a3938c;
--luncher-border: var(--ck-soft-line);
--luncher-border-light: #f4e9e4;
--luncher-navbar-bg: #ffe3ea;
--luncher-navbar-text: var(--ck-ink);
--luncher-action-icon: var(--ck-pink-deep);
--luncher-buyer-icon: #4a78b5;
--luncher-success: #5a9e4a;
--luncher-warning: #d9a21a;
--luncher-danger: #d9534f;
--luncher-radius-sm: 14px;
--luncher-radius: 18px;
--luncher-radius-lg: 22px;
--luncher-radius-xl: 26px;
--luncher-shadow: 0 3px 0 var(--ck-line);
--luncher-shadow-lg: 0 5px 0 var(--ck-line), 0 10px 22px rgb(74 63 58 / 0.15);
&[data-bs-theme="dark"] {
// Noc pod hvězdami — postavičky už spí
--ck-line: #12152b;
--ck-soft-line: #3a4170;
--ck-white-pink: #3a2f4a;
--ck-blue-light: #283a5c;
--ck-yellow-light: #463f2a;
--luncher-primary: #ff9fb8;
--luncher-primary-hover: #ffbfcf;
--luncher-primary-light: #3d2a42;
--luncher-bg: #1b1f3a;
--luncher-bg-card: #262b4d;
--luncher-bg-hover: #2f355c;
--luncher-text: #f4eef6;
--luncher-text-secondary: #cfc8dc;
--luncher-text-muted: #8e8aab;
--luncher-border-light: #2f355c;
--luncher-navbar-bg: #2a2f55;
--luncher-navbar-text: #f4eef6;
--luncher-action-icon: #ff9fb8;
--luncher-buyer-icon: #9cc3f0;
--luncher-success: #9fd18b;
--luncher-warning: #f7e08a;
--luncher-danger: #ff8a8a;
--luncher-shadow: 0 3px 0 var(--ck-line), 0 6px 16px rgb(0 0 0 / 0.3);
--luncher-shadow-lg: 0 5px 0 var(--ck-line), 0 0 24px rgb(255 159 184 / 0.25);
}
body {
font-family: var(--ck-font);
background-color: var(--luncher-bg);
// Pastelová louka — nahoře růžové nebe, dole trsy trávy s kytičkami
background-image:
#{$ck-grass},
radial-gradient(circle at 15% 0%, rgb(247 168 184 / 0.25), transparent 40%),
radial-gradient(circle at 85% 10%, rgb(122 163 214 / 0.18), transparent 40%),
linear-gradient(180deg, #fff8f2 60%, #f2fae9);
background-size: 90px 28px, 100% 100%, 100% 100%, 100% 100%;
background-position: left bottom, 0 0, 0 0, 0 0;
background-repeat: repeat-x, no-repeat, no-repeat, no-repeat;
background-attachment: fixed;
}
&[data-bs-theme="dark"] body {
// Hvězdná noc s měsícem
background-image:
radial-gradient(circle at 88% 8%, #fff6c8 0 22px, rgb(255 246 200 / 0.25) 24px, transparent 70px),
#{$ck-stars},
linear-gradient(180deg, #1b1f3a, #232a52);
background-size: 100% 100%, 160px 160px, 100% 100%;
background-repeat: no-repeat, repeat, no-repeat;
}
.app-container {
background: transparent;
}
h1, h2, h3, h4, .navbar-brand {
font-family: var(--ck-display);
font-weight: 400;
}
.btn {
font-family: var(--ck-font);
}
// ---------- Navbar: pastelově růžový, u loga obličej Chiikawy ----------
.navbar {
background: var(--luncher-navbar-bg) !important;
border-bottom: 2.5px solid var(--ck-line);
box-shadow: 0 3px 0 rgb(74 63 58 / 0.12);
.navbar-brand {
letter-spacing: 0.02em;
&::after {
content: '';
display: inline-block;
width: 26px;
height: 26px;
margin-left: 6px;
margin-right: 0.6em;
vertical-align: -5px;
background: $ck-chiikawa center / contain no-repeat;
transform-origin: 50% 100%;
animation: ck-wobble 3.2s ease-in-out infinite;
}
}
.dropdown-menu {
border: 2px solid var(--ck-line);
border-radius: var(--luncher-radius);
box-shadow: var(--luncher-shadow);
}
}
&[data-bs-theme="dark"] .navbar {
border-bottom-color: #3a4170;
box-shadow: 0 3px 14px rgb(0 0 0 / 0.35);
}
// ---------- Titulek dne: mochi štítek s ruměncem, zpoza vykukuje Chiikawa ----------
.day-navigator .title {
position: relative;
display: inline-block;
padding: 8px 40px 8px;
margin-top: 14px;
font-family: var(--ck-display);
font-size: 1.6rem;
color: var(--ck-ink) !important;
background:
radial-gradient(ellipse 9px 5px at 26px 58%, var(--ck-pink) 98%, transparent),
radial-gradient(ellipse 9px 5px at calc(100% - 26px) 58%, var(--ck-pink) 98%, transparent),
#ffffff;
border: 2.5px solid var(--ck-line);
border-radius: 999px;
box-shadow: 0 4px 0 var(--ck-line);
// Chiikawa vykukuje zpoza štítku
&::after {
content: '';
position: absolute;
right: 26px;
top: -27px;
width: 34px;
height: 34px;
background: $ck-chiikawa center / contain no-repeat;
// Spodní část, která by překryla štítek, se ořízne — vypadá to, že je schovaná za ním
clip-path: inset(0 0 7px 0);
animation: ck-peek 4s ease-in-out infinite;
}
// Jiskřička radosti
&::before {
content: '✦';
position: absolute;
left: -18px;
top: -10px;
font-size: 0.7em;
color: #e0b93a;
animation: ck-twinkle 1.8s ease-in-out infinite;
}
&.text-muted {
color: var(--luncher-text-muted) !important;
background: #f6efeb;
border-color: #c9bab3;
box-shadow: 0 4px 0 #c9bab3;
// Jiný den — Chiikawa spí a nevykukuje
&::after,
&::before {
display: none;
}
}
}
&[data-bs-theme="dark"] .day-navigator .title {
color: #f4eef6 !important;
background:
radial-gradient(ellipse 9px 5px at 26px 58%, rgb(247 168 184 / 0.7) 98%, transparent),
radial-gradient(ellipse 9px 5px at calc(100% - 26px) 58%, rgb(247 168 184 / 0.7) 98%, transparent),
#3a4170;
border-color: #12152b;
box-shadow: 0 4px 0 #12152b, 0 0 22px rgb(255 159 184 / 0.25);
&.text-muted {
color: var(--luncher-text-muted) !important;
background: #262b4d;
box-shadow: 0 4px 0 #12152b;
}
}
// ---------- Karty podniků: střídají se Chiikawa, Hachiware a Usagi ----------
.restaurant-card {
--ck-card-bg: var(--ck-white-pink);
--ck-card-face: #{$ck-chiikawa};
border: 2.5px solid var(--ck-line);
border-radius: var(--luncher-radius-lg);
box-shadow: var(--luncher-shadow);
overflow: hidden;
background: var(--luncher-bg-card);
transition: transform 0.2s cubic-bezier(.3, 1.6, .5, 1), box-shadow 0.2s ease;
&:hover {
transform: translateY(-3px);
box-shadow: var(--luncher-shadow-lg);
}
.restaurant-header {
background: var(--ck-card-bg);
border-bottom: 2.5px solid var(--ck-line);
// Obličej postavy vpravo v hlavičce
&::after {
content: '';
flex: none;
width: 38px;
height: 38px;
background: var(--ck-card-face) center / contain no-repeat;
transition: transform 0.25s cubic-bezier(.3, 1.6, .5, 1);
}
&:hover {
background: var(--ck-card-bg);
filter: brightness(0.97);
&::after {
transform: translateY(-3px) rotate(-8deg);
}
}
h3 {
color: var(--ck-ink);
font-family: var(--ck-display);
font-weight: 400;
}
small {
color: #8a7a73;
}
}
}
[class*="col"]:nth-child(3n+2) > .restaurant-card {
--ck-card-bg: var(--ck-blue-light);
--ck-card-face: #{$ck-hachiware};
}
[class*="col"]:nth-child(3n) > .restaurant-card {
--ck-card-bg: var(--ck-yellow-light);
--ck-card-face: #{$ck-usagi};
}
&[data-bs-theme="dark"] .restaurant-card {
border-color: #12152b;
.restaurant-header {
border-bottom-color: #12152b;
h3 {
color: #f4eef6;
}
small {
color: #b4aecb;
}
}
}
.food-table {
--bs-table-bg: transparent;
}
.food-table tbody tr:hover > td {
background: #fff1f4;
}
&[data-bs-theme="dark"] .food-table tbody tr:hover > td {
background: #2f355c;
}
.food-price {
color: var(--ck-pink-deep);
font-weight: 900;
}
&[data-bs-theme="dark"] .food-price {
color: #ff9fb8;
}
// ---------- Panely a tabulka voleb ----------
.choice-section,
.choices-table,
.pizza-section,
.no-votes {
border: 2.5px solid var(--ck-line);
border-radius: var(--luncher-radius-lg);
box-shadow: var(--luncher-shadow);
}
.choices-table,
.choices-table .nested-table {
--bs-table-bg: var(--luncher-bg-card);
}
.choices-table {
border-collapse: separate;
border-spacing: 0;
}
// Sloupec s podnikem: růžová tvářička
.choices-table > tbody > tr > td:first-child {
background: var(--ck-white-pink);
color: var(--ck-pink-deep);
font-family: var(--ck-display);
border-right: 2.5px dashed var(--ck-pink);
}
&[data-bs-theme="dark"] {
.choice-section,
.choices-table,
.pizza-section,
.no-votes,
.card {
border-color: #12152b;
}
.choices-table > tbody > tr > td:first-child {
color: #ff9fb8;
}
}
// Bootstrap karty (skupinové objednávky apod.) a seznamy
.card {
border: 2.5px solid var(--ck-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(--ck-white-pink);
border-bottom: 2px dashed var(--ck-pink);
font-family: var(--ck-display);
&:first-child {
border-radius: calc(var(--luncher-radius-lg) - 2px) calc(var(--luncher-radius-lg) - 2px) 0 0;
}
}
.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: 2px solid var(--ck-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(--ck-soft-line);
&:focus {
border-color: var(--ck-pink-deep);
box-shadow: 0 0 0 3px rgb(247 168 184 / 0.45);
}
}
// ---------- Tlačítka: nálepky ve tvaru mochi, při najetí poskočí ----------
.btn {
border-radius: 999px;
font-weight: 900;
letter-spacing: 0.02em;
transition: transform 0.18s cubic-bezier(.3, 1.6, .5, 1), box-shadow 0.18s ease, background 0.2s ease;
}
.btn-primary {
background: var(--ck-pink-deep);
border: 2px solid var(--ck-line);
color: #fff;
box-shadow: 0 3px 0 var(--ck-line);
&:hover {
background: #d6608a;
border-color: var(--ck-line);
color: #fff;
transform: translateY(-2px);
box-shadow: 0 5px 0 var(--ck-line);
}
&:active {
transform: translateY(2px) !important;
box-shadow: 0 1px 0 var(--ck-line);
}
&:disabled {
background: #f1e6e2;
border-color: #c9bab3;
color: #a3938c;
box-shadow: none;
}
}
.btn-outline-primary {
background: var(--luncher-bg-card);
border: 2px solid var(--ck-line);
color: var(--ck-pink-deep);
box-shadow: 0 3px 0 var(--ck-line);
&:hover {
background: var(--ck-white-pink);
border-color: var(--ck-line);
color: var(--ck-pink-deep);
transform: translateY(-2px);
}
}
// Sekundární — modrá jako Hachiware
.btn-secondary {
background: var(--ck-blue);
border: 2px solid var(--ck-line);
color: #fff;
box-shadow: 0 3px 0 var(--ck-line);
&:hover {
background: #6a93c8;
border-color: var(--ck-line);
color: #fff;
transform: translateY(-2px);
}
}
.btn-danger {
border: 2px solid var(--ck-line);
box-shadow: 0 3px 0 var(--ck-line);
}
.action-icon:hover {
background: var(--luncher-primary-light);
color: var(--luncher-primary);
}
// Tmavý režim: světlé pastelové akcenty potřebují tmavý text
&[data-bs-theme="dark"] {
.btn-primary {
background: #ff9fb8;
color: #2a1f33;
&:hover {
background: #ffbfcf;
color: #2a1f33;
}
&:disabled {
background: #2f355c;
border-color: #12152b;
color: #8e8aab;
}
}
.btn-outline-primary {
color: #ff9fb8;
&:hover {
background: #3d2a42;
color: #ff9fb8;
}
}
.btn-secondary {
background: #9cc3f0;
color: #1b1f3a;
&:hover {
background: #b6d4f5;
color: #1b1f3a;
}
}
.react-datepicker__day--selected,
.react-datepicker__day--keyboard-selected {
color: #2a1f33;
}
}
// ---------- Modaly: velká nálepka s obrysem ----------
.modal-content {
border: 2.5px solid var(--ck-line);
border-radius: var(--luncher-radius-xl);
box-shadow: 0 6px 0 var(--ck-line), 0 18px 40px rgb(74 63 58 / 0.25);
overflow: hidden;
}
.modal-header {
background: var(--luncher-navbar-bg);
border-bottom: 2.5px solid var(--ck-line);
.modal-title, .modal-title h2 {
color: var(--ck-ink);
font-family: var(--ck-display);
font-weight: 400;
}
}
&[data-bs-theme="dark"] {
.modal-content {
border-color: #12152b;
box-shadow: 0 6px 0 #12152b, 0 0 30px rgb(255 159 184 / 0.15);
}
.modal-header {
border-bottom-color: #12152b;
.modal-title, .modal-title h2 {
color: #f4eef6;
}
}
}
// Patička — pruh trávy, kde se pleje plevel
.footer {
border-top: 2.5px solid var(--ck-line);
background:
#{$ck-grass} left top / 90px 28px repeat-x,
var(--luncher-navbar-bg);
padding-top: 34px;
a {
color: var(--ck-pink-deep);
}
}
&[data-bs-theme="dark"] .footer {
border-top-color: #12152b;
background: var(--luncher-navbar-bg);
padding-top: 16px;
a {
color: #ff9fb8;
}
}
}
// Chiikawa se kolébá ze strany na stranu
@keyframes ck-wobble {
0%, 100% { transform: rotate(0deg); }
20% { transform: rotate(-10deg); }
40% { transform: rotate(8deg); }
55% { transform: rotate(0deg); }
}
// Vykukování zpoza štítku — schová se a zase vykoukne (ořez se posouvá s ní)
@keyframes ck-peek {
0%, 15% { transform: translateY(18px); clip-path: inset(0 0 25px 0); }
30%, 75% { transform: translateY(0); clip-path: inset(0 0 7px 0); }
85% { transform: translateY(-2px); clip-path: inset(0 0 5px 0); }
100% { transform: translateY(18px); clip-path: inset(0 0 25px 0); }
}
@keyframes ck-twinkle {
0%, 100% { transform: scale(0.7) rotate(0deg); opacity: 0.4; }
50% { transform: scale(1.15) rotate(20deg); opacity: 1; }
}
@media (max-width: 575.98px) {
#{$ck} .day-navigator .title {
font-size: 1.25rem;
padding: 6px 30px;
}
}
@media (prefers-reduced-motion: reduce) {
#{$ck} .navbar .navbar-brand::after,
#{$ck} .day-navigator .title::after,
#{$ck} .day-navigator .title::before {
animation: none;
}
}