Styles refactor

This commit is contained in:
Renan LE CARO 2025-03-14 16:13:43 +01:00
parent 2e3ab3011f
commit 7e3750c915
9 changed files with 419 additions and 416 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

120
dist/index.html vendored
View file

@ -49,7 +49,7 @@ body {
top: 0; top: 0;
} }
#score:hover, #score:focus, #menu:hover, #menu:focus { #score:hover, #menu:hover, #score:focus, #menu:focus {
cursor: pointer; cursor: pointer;
background: #0000004d; background: #0000004d;
} }
@ -88,15 +88,6 @@ body {
display: flex; display: flex;
} }
.popup.actionsAsGrid > div {
max-width: 800px;
& section {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
display: grid;
}
}
.popup > div > * { .popup > div > * {
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -111,50 +102,57 @@ body {
align-items: stretch; align-items: stretch;
margin-top: 20px; margin-top: 20px;
display: flex; display: flex;
}
& button { .popup > div > section button {
font: inherit; font: inherit;
color: #fff; color: #fff;
cursor: pointer; cursor: pointer;
text-align: left; text-align: left;
background: #000c; background: #000c;
border: 1px solid #fff; border: 1px solid #fff;
gap: 10px; gap: 10px;
margin-top: -1px; margin-top: -1px;
padding: 10px; padding: 10px;
display: flex; display: flex;
}
&:not([disabled]):hover, &:not([disabled]):focus { .popup > div > section button:not([disabled]):hover, .popup > div > section button:not([disabled]):focus {
z-index: 1; z-index: 1;
border-color: #f1d33b; border-color: #f1d33b;
position: relative; position: relative;
} }
&[disabled] { .popup > div > section button[disabled] {
opacity: .5; opacity: .5;
filter: saturate(0); filter: saturate(0);
pointer-events: none; pointer-events: none;
} }
& > div { .popup > div > section button > div {
flex-grow: 1; flex-grow: 1;
} }
& > div > em { .popup > div > section button > div > em {
opacity: .8; opacity: .8;
display: block; display: block;
} }
&.grey-out-unless-hovered { .popup > div > section button.grey-out-unless-hovered:not(:hover) {
&:not(:hover) { opacity: .6;
opacity: .6; }
& img { .popup > div > section button.grey-out-unless-hovered:not(:hover) img {
filter: saturate(0); filter: saturate(0);
} }
}
} .popup.actionsAsGrid > div {
} max-width: 800px;
}
.popup.actionsAsGrid > div section {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
display: grid;
} }
.popup button.close-modale { .popup button.close-modale {
@ -168,21 +166,21 @@ body {
top: 0; top: 0;
right: 0; right: 0;
overflow: hidden; overflow: hidden;
}
&:before { .popup button.close-modale:before {
content: "+"; content: "+";
font-size: 80px; font-size: 80px;
display: inline-block; display: inline-block;
position: absolute; position: absolute;
top: 34px; top: 34px;
left: 26px; left: 26px;
transform: translate(-50%, -50%)rotate(45deg); transform: translate(-50%, -50%)rotate(45deg);
} }
&:hover { .popup button.close-modale:hover {
background: #000; background: #000;
font-weight: bold; font-weight: bold;
}
} }
.popup .textAfterButtons { .popup .textAfterButtons {
@ -238,7 +236,7 @@ body {
margin: 40px; margin: 40px;
} }
#level-recording-container img, #level-recording-container video { #level-recording-container video {
max-width: 100%; max-width: 100%;
height: auto; height: auto;
} }

329
src/game.less Normal file
View file

@ -0,0 +1,329 @@
* {
font-family: Courier New,
Courier,
Lucida Sans Typewriter,
Lucida Typewriter,
monospace;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
width: 100vw;
height: 100vh;
height: calc(var(--vh, 1vh) * 100);
color: white;
background-size: 120px 120px;
background-color: var(--background1);
--background1: #030c23;
--background2: #03112a;
}
#game {
position: absolute;
top: 0;
left: 0;
height: 100vh;
height: calc(var(--vh, 1vh) * 100);
width: 100vw;
}
#score,
#menu {
position: absolute;
top: 0;
z-index: 1;
padding: 10px;
appearance: none;
background: none;
border: none;
font: inherit;
color: white;
min-width: 40px;
min-height: 40px;
line-height: 20px;
&:hover,
&:focus {
background: rgba(0, 0, 0, 0.3);
cursor: pointer;
}
}
#score {
right: 0;
}
#menu {
left: 0;
@media screen and (orientation: portrait) {
& > span {
display: none;
}
}
}
.popup {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.9);
z-index: 10;
display: flex;
overflow: auto;
& > div {
margin: auto;
padding: 20px 10px;
transform-origin: center;
display: flex;
flex-direction: column;
align-items: stretch;
width: 100%;
max-width: 450px;
& > * {
padding: 0;
margin: 0;
}
& > h2,
& > p {
margin-bottom: 20px;
}
& > section {
display: flex;
flex-direction: column;
align-items: stretch;
margin-top: 20px;
button {
font: inherit;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px;
cursor: pointer;
border: 1px solid white;
text-align: left;
display: flex;
gap: 10px;
margin-top: -1px;
&:not([disabled]):hover,
&:not([disabled]):focus {
border-color: #f1d33b;
position: relative;
z-index: 1;
}
&[disabled] {
opacity: 0.5;
filter: saturate(0);
pointer-events: none;
}
& > div {
flex-grow: 1;
}
& > div > em {
display: block;
opacity: 0.8;
}
&.grey-out-unless-hovered {
&:not(:hover) {
opacity: 0.6;
img {
filter: saturate(0);
}
}
}
}
}
}
&.actionsAsGrid > div {
max-width: 800px;
section {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
}
button.close-modale {
color: white;
position: absolute;
top: 0;
right: 0;
width: 60px;
height: 60px;
background: transparent;
border: none;
cursor: pointer;
overflow: hidden;
&:before {
content: "+";
position: absolute;
transform: translate(-50%, -50%) rotate(45deg);
font-size: 80px;
display: inline-block;
top: 34px;
left: 26px;
}
&:hover {
font-weight: bold;
background: black;
}
}
.textAfterButtons {
color: rgba(255, 255, 255, 0.58);
}
a[href] {
color: inherit;
&:hover,
&:focus {
color: white;
}
}
}
/*Unlocks progress bar*/
.progress {
display: block;
padding: 5px 10px;
background: #1c1c2f;
color: #fff;
box-shadow: inset 3px 3px 5px rgba(0, 0, 0, 0.5);
border-radius: 5px;
text-align: center;
position: relative;
overflow: hidden;
& > .progress_bar_part {
display: block;
background: #4049ca;
box-shadow: inset 3px 3px 5px rgba(0, 0, 0, 0.5);
left: 0;
position: absolute;
right: 0;
top: 0;
bottom: 0;
transform-origin: top left;
animation: grow 1s both ease-out;
z-index: 1;
}
& > span {
display: block;
position: relative;
z-index: 2;
}
@keyframes grow {
0% {
transform: scale(0, 1);
}
}
}
#level-recording-container {
max-width: 400px;
text-align: center;
margin: 40px;
video {
max-width: 100%;
height: auto;
}
a {
display: block;
video {
border-radius: 10px;
display: block;
outline: 1px solid white;
box-shadow: 2px 2px 5px black;
margin: 20px auto;
}
}
@media (min-width: 1200px) {
position: absolute;
top: 40px;
left: 40px;
max-width: calc((100vw - 450px) / 2 - 80px);
}
}
.histogram {
display: flex;
gap: 10px;
align-items: stretch;
margin: 10px 0 40px 0;
& > span {
/* Hover zone */
flex-grow: 1;
width: 10px;
position: relative;
display: flex;
flex-direction: column;
justify-content: flex-end;
&.active > span {
background: #4049ca;
}
& > span {
/*Visible bar*/
background: #1c1c2f;
width: 100%;
display: block;
border-radius: 5px;
min-height: 1px;
& > span {
/*label */
position: absolute;
bottom: -20px;
pointer-events: none;
white-space: nowrap;
transform-origin: bottom left;
font-size: 13px;
text-align: center;
display: block;
left: 50%;
transform: translate(-50%, 0);
}
}
}
&> span:not(:hover):not(.active) > span > span {
opacity: 0;
}
}
h2.histogram-title {
color: #3b3f75;
font-size: 18px;
}
h2.histogram-title strong {
color: #4049ca;
}

View file

@ -1,4 +1,5 @@
import {getMajorityValue, sample, sumOfKeys} from "./game_utils"; import {getMajorityValue, makeEmptyPerksMap, sample, sumOfKeys} from "./game_utils";
import {Upgrade} from "./types";
describe('getMajorityValue', ()=>{ describe('getMajorityValue', ()=>{
@ -40,3 +41,9 @@ describe('sumOfKeys', ()=>{
expect(sumOfKeys(null)).toEqual(0) expect(sumOfKeys(null)).toEqual(0)
}) })
}) })
describe('makeEmptyPerksMap', ()=>{
it('returns an object',()=>{
expect(makeEmptyPerksMap([{id:"ball_attract_ball"}])).toEqual({ball_attract_ball:0})
expect(makeEmptyPerksMap([])).toEqual({})
})
})

View file

@ -1,5 +1,5 @@
import {PerksMap, Upgrade} from "./types"; import {PerkId, PerksMap, Upgrade} from "./types";
export function getMajorityValue(arr: string[]): string { export function getMajorityValue(arr: string[]): string {
const count: { [k: string]: number } = {}; const count: { [k: string]: number } = {};
@ -19,7 +19,7 @@ export function sumOfKeys(obj:{[key:string]:number} | undefined | null){
return Object.values(obj)?.reduce((a,b)=>a+b,0) ||0 return Object.values(obj)?.reduce((a,b)=>a+b,0) ||0
} }
export const makeEmptyPerksMap = (upgrades:Upgrade[]) => { export const makeEmptyPerksMap = (upgrades: { id:PerkId }[]) => {
const p = {} as any; const p = {} as any;
upgrades.forEach((u) => (p[u.id] = 0)); upgrades.forEach((u) => (p[u.id] = 0));
return p as PerksMap; return p as PerksMap;

View file

@ -1,15 +1,5 @@
import {RawLevel} from "./types"; import {RawLevel} from "./types";
export function hashCode(string: string) {
let hash = 0;
for (let i = 0; i < string.length; i++) {
let code = string.charCodeAt(i);
hash = (hash << 5) - hash + code;
hash = hash & hash; // Convert to 32bit integer
}
return Math.abs(hash);
}
import _backgrounds from "./backgrounds.json"; import _backgrounds from "./backgrounds.json";
const backgrounds = _backgrounds as string[]; const backgrounds = _backgrounds as string[];
@ -22,3 +12,14 @@ export function getLevelBackground(level:RawLevel){
} }
return svg return svg
} }
export function hashCode(string: string) {
let hash = 0;
for (let i = 0; i < string.length; i++) {
let code = string.charCodeAt(i);
hash = (hash << 5) - hash + code;
hash = hash & hash; // Convert to 32bit integer
}
return Math.abs(hash);
}

View file

@ -13,7 +13,7 @@
content="A breakout game with roguelite mechanics. Break bricks, catch coins, pick upgrades, repeat. Play for free on mobile and desktop." content="A breakout game with roguelite mechanics. Break bricks, catch coins, pick upgrades, repeat. Play for free on mobile and desktop."
/> />
<style> <style>
@import "style.css"; @import "game.less";
</style> </style>
<link <link
rel="icon" rel="icon"

View file

@ -1,332 +0,0 @@
* {
font-family:
Courier New,
Courier,
Lucida Sans Typewriter,
Lucida Typewriter,
monospace;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
width: 100vw;
height: 100vh;
height: calc(var(--vh, 1vh) * 100);
color: white;
background-size: 120px 120px;
background-color: var(--background1);
--background1: #030c23;
--background2: #03112a;
}
#game {
position: absolute;
top: 0;
left: 0;
height: 100vh;
height: calc(var(--vh, 1vh) * 100);
width: 100vw;
}
#score,
#menu {
position: absolute;
top: 0;
z-index: 1;
padding: 10px;
appearance: none;
background: none;
border: none;
font: inherit;
color: white;
min-width: 40px;
min-height: 40px;
line-height: 20px;
}
#score:hover,
#score:focus,
#menu:hover,
#menu:focus {
background: rgba(0, 0, 0, 0.3);
cursor: pointer;
}
#score {
right: 0;
}
#menu {
left: 0;
}
@media screen and (orientation: portrait) {
#menu > span {
display: none;
}
}
.popup {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.9);
z-index: 10;
display: flex;
overflow: auto;
}
.popup > div {
margin: auto;
padding: 20px 10px;
/*border: 1px solid white;*/
transform-origin: center;
display: flex;
flex-direction: column;
align-items: stretch;
width: 100%;
max-width: 450px;
}
.popup.actionsAsGrid > div {
max-width: 800px;
section {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
}
.popup > div > * {
padding: 0;
margin: 0;
}
.popup > div > h2,
.popup > div > p {
margin-bottom: 20px;
}
.popup > div > section {
display: flex;
flex-direction: column;
align-items: stretch;
margin-top: 20px;
button {
font: inherit;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px;
cursor: pointer;
border: 1px solid white;
text-align: left;
display: flex;
gap: 10px;
margin-top: -1px;
&:not([disabled]):hover,
&:not([disabled]):focus {
border-color: #f1d33b;
position: relative;
z-index: 1;
}
&[disabled] {
/*border: 1px solid #666;*/
opacity: 0.5;
filter: saturate(0);
pointer-events: none;
}
& > div {
flex-grow: 1;
}
& > div > em {
display: block;
opacity: 0.8;
}
&.grey-out-unless-hovered {
&:not(:hover) {
opacity: 0.6;
img {
filter: saturate(0);
}
}
}
}
}
.popup button.close-modale {
color: white;
position: absolute;
top: 0;
right: 0;
width: 60px;
height: 60px;
background: transparent;
border: none;
cursor: pointer;
overflow: hidden;
&:before {
content: "+";
position: absolute;
transform: translate(-50%, -50%) rotate(45deg);
font-size: 80px;
display: inline-block;
top: 34px;
left: 26px;
}
&:hover {
font-weight: bold;
background: black;
}
}
.popup .textAfterButtons {
color: rgba(255, 255, 255, 0.58);
}
.popup a[href] {
color: inherit;
}
.popup a[href]:hover,
.popup a[href]:focus {
color: white;
}
/*Unlocks progress bar*/
.progress {
display: block;
padding: 5px 10px;
background: #1c1c2f;
color: #fff;
box-shadow: inset 3px 3px 5px rgba(0, 0, 0, 0.5);
border-radius: 5px;
text-align: center;
position: relative;
overflow: hidden;
}
.progress > .progress_bar_part {
display: block;
background: #4049ca;
box-shadow: inset 3px 3px 5px rgba(0, 0, 0, 0.5);
left: 0;
position: absolute;
right: 0;
top: 0;
bottom: 0;
transform-origin: top left;
animation: grow 1s both ease-out;
z-index: 1;
}
.progress > span {
display: block;
position: relative;
z-index: 2;
}
@keyframes grow {
0% {
transform: scale(0, 1);
}
}
#level-recording-container {
max-width: 400px;
text-align: center;
margin: 40px;
}
#level-recording-container img,
#level-recording-container video {
max-width: 100%;
height: auto;
}
#level-recording-container a {
display: block;
}
#level-recording-container a video {
border-radius: 10px;
display: block;
outline: 1px solid white;
box-shadow: 2px 2px 5px black;
margin: 20px auto;
}
@media (min-width: 1200px) {
#level-recording-container {
position: absolute;
top: 40px;
left: 40px;
max-width: calc((100vw - 450px) / 2 - 80px);
}
}
.histogram {
display: flex;
gap: 10px;
align-items: stretch;
margin: 10px 0 40px 0;
}
.histogram > span {
/* Hover zone */
flex-grow: 1;
width: 10px;
position: relative;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.histogram > span.active > span {
background: #4049ca;
}
.histogram > span > span {
/*Visible bar*/
background: #1c1c2f;
width: 100%;
display: block;
border-radius: 5px;
min-height: 1px;
}
.histogram > span > span > span {
/*label */
position: absolute;
bottom: -20px;
pointer-events: none;
white-space: nowrap;
transform-origin: bottom left;
font-size: 13px;
text-align: center;
display: block;
left: 50%;
transform: translate(-50%, 0);
}
.histogram > span:not(:hover):not(.active) > span > span {
opacity: 0;
}
h2.histogram-title {
color: #3b3f75;
font-size: 18px;
}
h2.histogram-title strong {
color: #4049ca;
}