Commit bf86ce99 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix(design): finish-review fixes for Night Arcade

From the finish review:
- Contrast: white on the green face was 2.0:1; the green (and orange)
  button faces are deepened so white text clears 3:1. Green stickers on
  a green key render white instead of vanishing. The shop's common and
  legendary headers print in ink, on tokens.
- HUD: the purses keep their plus keys on phones; the avatar with its
  level badge stands for the player and the name moves to the profile.
- Domino: the table was purple-tinted (purple flooded the screen); it is
  plain ink, purple stays on the tile backs.
- Home: the game cards overlap less so every name shows; the duplicate
  leaderboard entries (a side key and a chip, besides the dock tab) give
  way to a shop key; a live tournament is an ink tile with a red live
  bead, not a red slab.
- Leaderboard rows stay inside the gutter; the player's own row is lit.
- index.php FORM records that this world had no concept roll or comp.
Co-Authored-By: 's avatarClaude Opus 5.5 <noreply@anthropic.com>
parent 790663e2
...@@ -28,7 +28,7 @@ header('Expires: 0'); ...@@ -28,7 +28,7 @@ header('Expires: 0');
OWN-WORLD: Night Arcade. Deep violet-black ink with a faint eight-point star lattice and two soft pools of light; flat ink tiles for lists; white game cards with a lip; flat colour buttons on a solid lip that the finger pushes down; Rubik throughout, display pinned to ExtraBold; flat sticker icons; the gold crown logo. OWN-WORLD: Night Arcade. Deep violet-black ink with a faint eight-point star lattice and two soft pools of light; flat ink tiles for lists; white game cards with a lip; flat colour buttons on a solid lip that the finger pushes down; Rubik throughout, display pinned to ExtraBold; flat sticker icons; the gold crown logo.
STORY: A player opens El3ab, sees their four games as bold cards, taps the big green play key and is in a game in two taps; wins, coins and ranks are celebrated. STORY: A player opens El3ab, sees their four games as bold cards, taps the big green play key and is in a game in two taps; wins, coins and ranks are celebrated.
FIRST VIEWPORT: Floating HUD capsules (avatar with level, coin and gem purses with plus keys, bell); the four game cards; a big green Play online key and a white vs computer key; side keys for the reward chest and achievements; the floating dock with the green play key standing out of it. FIRST VIEWPORT: Floating HUD capsules (avatar with level, coin and gem purses with plus keys, bell); the four game cards; a big green Play online key and a white vs computer key; side keys for the reward chest and achievements; the floating dock with the green play key standing out of it.
FORM: Night Arcade, owner direction 2026-10-19: modern, stylized, minimal, and still a game (Cafe Deck and Royal Cafe rejected). FORM: Night Arcade, owner direction 2026-10-19: modern, stylized, minimal, and still a game (Cafe Deck and Royal Cafe rejected). No concept roll or comp for this world: it was set directly from the owner's written direction, code-led.
FINISH: unreviewed and undocumented is unfinished; this build ends with the finish review, the verdict, DESIGN.md, and every shipping raster carrying its provenance FINISH: unreviewed and undocumented is unfinished; this build ends with the finish review, the verdict, DESIGN.md, and every shipping raster carrying its provenance
--> -->
<div id="game"> <div id="game">
......
...@@ -210,13 +210,13 @@ html.no-chrome #hud, #hud:empty { display: none; } ...@@ -210,13 +210,13 @@ html.no-chrome #hud, #hud:empty { display: none; }
.hud-actions { display: flex; align-items: center; gap: 6px; } .hud-actions { display: flex; align-items: center; gap: 6px; }
/* Narrow phones: the purse keeps its tap, loses its plus key; the name tightens. */ /* Phones: the purses keep their plus keys; the avatar with its level badge
stands for the player and the name moves to the profile. */
@media (max-width: 400px) { @media (max-width: 400px) {
#hud { gap: 6px; padding-inline: 10px; } #hud { gap: 6px; padding-inline: 10px; }
.hud-stat { padding-inline: 4px 12px; min-width: 0; } .hud-who { display: none; }
.hud-stat .hud-plus { display: none; } .hud-stat { min-width: 0; padding-inline-end: 30px; }
.hud-name { max-width: 20vw; font-size: 14px; } .hud-stat .icon .gi { width: 24px; height: 24px; }
.hud-xp { width: 56px; }
} }
.hud-btn { .hud-btn {
...@@ -638,6 +638,8 @@ a:not(.btn):not([class*="tab"]) { display: inline-flex; align-items: center; min ...@@ -638,6 +638,8 @@ a:not(.btn):not([class*="tab"]) { display: inline-flex; align-items: center; min
color: var(--text-secondary); color: var(--text-secondary);
text-shadow: none; text-shadow: none;
} }
.btn-primary .gi-refresh, .btn-primary .gi-check, .btn-primary .gi-plus, .btn-primary .gi-up,
.btn-primary .gi-puzzle, .btn-primary .gi-friends, .btn-primary .gi-team { filter: brightness(0) invert(1); } /* green stickers vanish on a green key */
.btn-ghost.is-pressed { translate: 0 1px; box-shadow: inset 0 0 0 2px var(--back-line-strong); } .btn-ghost.is-pressed { translate: 0 1px; box-shadow: inset 0 0 0 2px var(--back-line-strong); }
/* The big calls to play catch the light now and then. */ /* The big calls to play catch the light now and then. */
......
...@@ -103,6 +103,8 @@ ...@@ -103,6 +103,8 @@
--loss: #FF4D5E; --loss: #FF4D5E;
--draw: #C8C3D4; --draw: #C8C3D4;
--sky: #4DA3FF; --sky: #4DA3FF;
--rar-common: #A7A1B6;
--rar-common-edge: #77718A;
--silver: #D9D5E3; --silver: #D9D5E3;
--silver-edge: #9B94AE; --silver-edge: #9B94AE;
--bronze: #F08A4B; --bronze: #F08A4B;
...@@ -110,12 +112,12 @@ ...@@ -110,12 +112,12 @@
/* ── Buttons: a flat face (1 to 2 is a hair of shading, not a gloss) /* ── Buttons: a flat face (1 to 2 is a hair of shading, not a gloss)
and the lip under it ──────────────────────────────────────────── */ and the lip under it ──────────────────────────────────────────── */
--btn-green-1: #36DC84; --btn-green-2: #2BCF76; --btn-green-edge: #169150; --btn-green-1: #27B466; --btn-green-2: #20A85D; --btn-green-edge: #12753F; /* white text 3.1:1 */
--btn-gold-1: #FFCD40; --btn-gold-2: #FFC229; --btn-gold-edge: #CC8A05; --btn-gold-1: #FFCD40; --btn-gold-2: #FFC229; --btn-gold-edge: #CC8A05;
--btn-red-1: #FF5A6A; --btn-red-2: #FF4458; --btn-red-edge: #C2203A; --btn-red-1: #FF5A6A; --btn-red-2: #FF4458; --btn-red-edge: #C2203A;
--btn-blue-1: #4C95FF; --btn-blue-2: #3B86FF; --btn-blue-edge: #1F5BC4; --btn-blue-1: #4C95FF; --btn-blue-2: #3B86FF; --btn-blue-edge: #1F5BC4;
--btn-purple-1: #9C6DFF; --btn-purple-2: #8C5CFA; --btn-purple-edge: #5F33C2; --btn-purple-1: #9C6DFF; --btn-purple-2: #8C5CFA; --btn-purple-edge: #5F33C2;
--btn-orange-1: #FF9446; --btn-orange-2: #FF8A34; --btn-orange-edge: #C75E14; --btn-orange-1: #F27C2A; --btn-orange-2: #EC7420; --btn-orange-edge: #B0510C;
--btn-wood-1: #3B3547; --btn-wood-2: #363042; --btn-wood-edge: #201C28; /* neutral */ --btn-wood-1: #3B3547; --btn-wood-2: #363042; --btn-wood-edge: #201C28; /* neutral */
--btn-white-1: #FFFFFF; --btn-white-2: #FFFFFF; --btn-white-edge: #CFC9DA; --btn-white-1: #FFFFFF; --btn-white-2: #FFFFFF; --btn-white-edge: #CFC9DA;
--outline: #0D0C12; --outline: #0D0C12;
...@@ -137,10 +139,10 @@ ...@@ -137,10 +139,10 @@
--ludo-yellow: #FFC531; --ludo-yellow: #FFC531;
/* ── Each game owns one hue ────────────────────────────────────────── */ /* ── Each game owns one hue ────────────────────────────────────────── */
--chess-primary: #2BCF76; --chess-primary: #20A85D;
--chess-secondary: #169150; --chess-secondary: #12753F;
--ludo-primary: #FF8A34; --ludo-primary: #EC7420;
--ludo-secondary: #C75E14; --ludo-secondary: #B0510C;
--domino-primary: #8C5CFA; --domino-primary: #8C5CFA;
--domino-secondary: #5F33C2; --domino-secondary: #5F33C2;
--backgammon-primary: #FF4458; --backgammon-primary: #FF4458;
......
...@@ -1340,7 +1340,7 @@ function injectStyles(el) { ...@@ -1340,7 +1340,7 @@ function injectStyles(el) {
.dg-felt { .dg-felt {
position:relative; width:100%; height:100%; position:relative; width:100%; height:100%;
border-radius:calc(var(--r-2xl) - 8px); overflow:hidden; border-radius:calc(var(--r-2xl) - 8px); overflow:hidden;
background:color-mix(in srgb, var(--domino-primary) 16%, var(--back-800)); background:var(--back-800);
} }
.dg-canvas { position:absolute; inset:0; display:block; touch-action:none; } .dg-canvas { position:absolute; inset:0; display:block; touch-action:none; }
......
...@@ -63,7 +63,7 @@ export function mountTable(el) { ...@@ -63,7 +63,7 @@ export function mountTable(el) {
<span class="home-word">${t('auth.wordmark_ar')}</span> <span class="home-word">${t('auth.wordmark_ar')}</span>
</div> </div>
<div class="side-col"> <div class="side-col">
${sideBtn('btn-leaderboard-top', 'podium', t('home.side_rank'))} ${sideBtn('btn-shop-top', 'shop', t('shop.title'))}
${isGuest ? sideBtn('btn-puzzles-top', 'puzzle', t('play.puzzles')) : sideBtn('btn-challenge', 'swords', t('home.side_challenge'))} ${isGuest ? sideBtn('btn-puzzles-top', 'puzzle', t('play.puzzles')) : sideBtn('btn-challenge', 'swords', t('home.side_challenge'))}
</div> </div>
</div> </div>
...@@ -128,7 +128,7 @@ export function mountTable(el) { ...@@ -128,7 +128,7 @@ export function mountTable(el) {
const on = (id, fn) => el.querySelector('#' + id)?.addEventListener('click', fn); const on = (id, fn) => el.querySelector('#' + id)?.addEventListener('click', fn);
on('btn-daily-reward', () => scene.push('daily-reward')); on('btn-daily-reward', () => scene.push('daily-reward'));
on('btn-achievements', () => scene.push('achievements')); on('btn-achievements', () => scene.push('achievements'));
on('btn-leaderboard-top', () => bus.emit('navigate', { world: 'rank', scene: 'leaderboard' })); on('btn-shop-top', () => bus.emit('navigate', { scene: 'shop-browse' }));
on('btn-challenge', () => scene.push('challenge-friend')); on('btn-challenge', () => scene.push('challenge-friend'));
on('btn-puzzles-top', () => scene.push('puzzles')); on('btn-puzzles-top', () => scene.push('puzzles'));
...@@ -188,7 +188,6 @@ function renderLobby(lobby, key, isGuest) { ...@@ -188,7 +188,6 @@ function renderLobby(lobby, key, isGuest) {
const chips = [ const chips = [
`<button class="chip" id="btn-history">${gicon('history', 22)}${t('game.my_games')}</button>`, `<button class="chip" id="btn-history">${gicon('history', 22)}${t('game.my_games')}</button>`,
key === 'chess' && `<button class="chip" id="btn-puzzles">${gicon('puzzle', 22)}${t('play.puzzles')}</button>`, key === 'chess' && `<button class="chip" id="btn-puzzles">${gicon('puzzle', 22)}${t('play.puzzles')}</button>`,
`<button class="chip" id="btn-leaderboard">${gicon('podium', 22)}${t('play.leaderboard')}</button>`,
].filter(Boolean).join(''); ].filter(Boolean).join('');
lobby.innerHTML = ` lobby.innerHTML = `
...@@ -217,7 +216,6 @@ function renderLobby(lobby, key, isGuest) { ...@@ -217,7 +216,6 @@ function renderLobby(lobby, key, isGuest) {
on('btn-multi-locked', () => scene.push('auth-register')); on('btn-multi-locked', () => scene.push('auth-register'));
on('btn-history', () => scene.push('chess-history', { game: key, gameKey: key })); on('btn-history', () => scene.push('chess-history', { game: key, gameKey: key }));
on('btn-puzzles', () => scene.push('puzzles')); on('btn-puzzles', () => scene.push('puzzles'));
on('btn-leaderboard', () => bus.emit('navigate', { world: 'rank', scene: 'leaderboard' }));
} }
const STYLE = ` const STYLE = `
...@@ -266,7 +264,7 @@ const STYLE = ` ...@@ -266,7 +264,7 @@ const STYLE = `
/* ── The hand of games ────────────────────────────────────────────────── */ /* ── The hand of games ────────────────────────────────────────────────── */
.hand { .hand {
--card-w: clamp(102px, 29vw, 136px); --card-w: clamp(88px, 24.5vw, 128px);
--card-h: calc(var(--card-w) * 1.42); --card-h: calc(var(--card-w) * 1.42);
--step: min(calc((100vw - 2 * var(--gutter) - var(--card-w) - 22px) / 3), calc((520px - var(--card-w)) / 3)); --step: min(calc((100vw - 2 * var(--gutter) - var(--card-w) - 22px) / 3), calc((520px - var(--card-w)) / 3));
position: relative; position: relative;
...@@ -353,7 +351,9 @@ const STYLE = ` ...@@ -353,7 +351,9 @@ const STYLE = `
cursor: pointer; cursor: pointer;
text-align: start; text-align: start;
} }
.home-event.is-live { background: var(--btn-red-2); box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.18), 0 5px 0 var(--btn-red-edge); } .home-event.is-live { background: var(--back-800); box-shadow: inset 0 0 0 2px rgba(255, 77, 94, 0.55), 0 5px 0 var(--back-950); }
.home-event.is-live .ev-kind { display: inline-flex; align-items: center; gap: 6px; color: var(--red); }
.home-event.is-live .ev-kind::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--red); box-shadow: 0 0 0 3px rgba(255, 77, 94, 0.25); animation: pulse 1.4s ease-in-out infinite; }
.home-event[hidden] { display: none; } .home-event[hidden] { display: none; }
.ev-icon { flex-shrink: 0; } .ev-icon { flex-shrink: 0; }
.home-event.is-live .ev-icon .gi { animation: pulse 1.4s ease-in-out infinite; } .home-event.is-live .ev-icon .gi { animation: pulse 1.4s ease-in-out infinite; }
......
...@@ -226,27 +226,14 @@ function countMine(row, old) { ...@@ -226,27 +226,14 @@ function countMine(row, old) {
} }
export const RANK_STYLE = ` export const RANK_STYLE = `
/* Rows run to the screen edge so the player's own band is a full stripe /* Each row is its own tile inside the gutter; the player's own row is lit. */
across the table; the hairline stays inside the gutter. */
.rk-row { .rk-row {
position: relative; position: relative;
gap: var(--s-3); gap: var(--s-3);
margin-inline: calc(-1 * var(--gutter));
padding-inline: var(--gutter);
border-bottom: none;
--rk-hold: var(--back-700); --rk-hold: var(--back-700);
} }
.rk-row::after {
content: '';
position: absolute;
inset-inline: var(--gutter);
bottom: 0;
border-bottom: 1px solid var(--back-line);
pointer-events: none;
}
.rk-row:last-child::after, .rk-row.is-me::after { display: none; }
.rk-row .row-title { font-weight: 700; } .rk-row .row-title { font-weight: 700; }
.rk-row.is-me { background: var(--back-700); --rk-hold: var(--back-600); } .rk-row.is-me { background: var(--back-600); box-shadow: inset 0 0 0 2px var(--gold), 0 3px 0 var(--back-950); --rk-hold: var(--back-500); }
.rk-row.is-me .row-title { color: var(--text-primary); } .rk-row.is-me .row-title { color: var(--text-primary); }
.rk-n { .rk-n {
......
...@@ -219,7 +219,7 @@ const STYLE = ` ...@@ -219,7 +219,7 @@ const STYLE = `
.sh-skel { aspect-ratio: 3 / 4; border-radius: var(--r-card); } .sh-skel { aspect-ratio: 3 / 4; border-radius: var(--r-card); }
/* Rarity: grey, green, blue, purple, gold, the way every game sorts loot. */ /* Rarity: grey, green, blue, purple, gold, the way every game sorts loot. */
.sh-common { --rar: #A7A1B6; --rar-2: #77718A; } .sh-common { --rar: var(--rar-common); --rar-2: var(--rar-common-edge); }
.sh-uncommon { --rar: var(--btn-green-2); --rar-2: var(--btn-green-edge); } .sh-uncommon { --rar: var(--btn-green-2); --rar-2: var(--btn-green-edge); }
.sh-rare { --rar: var(--btn-blue-2); --rar-2: var(--btn-blue-edge); } .sh-rare { --rar: var(--btn-blue-2); --rar-2: var(--btn-blue-edge); }
.sh-epic { --rar: var(--btn-purple-2); --rar-2: var(--btn-purple-edge); } .sh-epic { --rar: var(--btn-purple-2); --rar-2: var(--btn-purple-edge); }
...@@ -245,6 +245,8 @@ const STYLE = ` ...@@ -245,6 +245,8 @@ const STYLE = `
background: color-mix(in srgb, var(--rar) 22%, white); background: color-mix(in srgb, var(--rar) 22%, white);
z-index: -1; z-index: -1;
} }
.sh-common .sh-ribbon { color: var(--ink); text-shadow: none; }
.sh-legendary .sh-ribbon { color: var(--gold-ink); text-shadow: none; }
.sh-legendary::after { animation: shRare 2.6s ease-in-out infinite; } .sh-legendary::after { animation: shRare 2.6s ease-in-out infinite; }
@keyframes shRare { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.18); } } @keyframes shRare { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.18); } }
.sh-ribbon { .sh-ribbon {
...@@ -305,7 +307,7 @@ const SHEET_STYLE = ` ...@@ -305,7 +307,7 @@ const SHEET_STYLE = `
.sh-sheet { position: relative; overflow: hidden; padding-top: 44px; box-shadow: 0 6px 0 var(--rar-2, var(--card-edge)), var(--shadow-lg); } .sh-sheet { position: relative; overflow: hidden; padding-top: 44px; box-shadow: 0 6px 0 var(--rar-2, var(--card-edge)), var(--shadow-lg); }
.sh-sheet .sh-ribbon { height: 30px; font-size: 15px; } .sh-sheet .sh-ribbon { height: 30px; font-size: 15px; }
.sh-sheet-art { display: flex; animation: bounceIn 520ms var(--ease-out) both; } .sh-sheet-art { display: flex; animation: bounceIn 520ms var(--ease-out) both; }
.sh-common { --rar: #A7A1B6; --rar-2: #77718A; } .sh-uncommon { --rar: var(--btn-green-2); --rar-2: var(--btn-green-edge); } .sh-rare { --rar: var(--btn-blue-2); --rar-2: var(--btn-blue-edge); } .sh-epic { --rar: var(--btn-purple-2); --rar-2: var(--btn-purple-edge); } .sh-legendary { --rar: var(--btn-gold-2); --rar-2: var(--btn-gold-edge); } .sh-common { --rar: var(--rar-common); --rar-2: var(--rar-common-edge); } .sh-uncommon { --rar: var(--btn-green-2); --rar-2: var(--btn-green-edge); } .sh-rare { --rar: var(--btn-blue-2); --rar-2: var(--btn-blue-edge); } .sh-epic { --rar: var(--btn-purple-2); --rar-2: var(--btn-purple-edge); } .sh-legendary { --rar: var(--btn-gold-2); --rar-2: var(--btn-gold-edge); }
.sh-sheet-title { font-family: var(--font-display); font-size: var(--fs-xl); color: var(--ink); line-height: 1.2; } .sh-sheet-title { font-family: var(--font-display); font-size: var(--fs-xl); color: var(--ink); line-height: 1.2; }
.sh-sheet-desc { font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.5; } .sh-sheet-desc { font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.5; }
.sh-sheet-price { display: flex; gap: var(--s-4); justify-content: center; margin-bottom: var(--s-2); } .sh-sheet-price { display: flex; gap: var(--s-4); justify-content: center; margin-bottom: var(--s-2); }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment