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

46
dist/index.html vendored
View file

@ -49,7 +49,7 @@ body {
top: 0;
}
#score:hover, #score:focus, #menu:hover, #menu:focus {
#score:hover, #menu:hover, #score:focus, #menu:focus {
cursor: pointer;
background: #0000004d;
}
@ -88,15 +88,6 @@ body {
display: flex;
}
.popup.actionsAsGrid > div {
max-width: 800px;
& section {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
display: grid;
}
}
.popup > div > * {
margin: 0;
padding: 0;
@ -111,8 +102,9 @@ body {
align-items: stretch;
margin-top: 20px;
display: flex;
}
& button {
.popup > div > section button {
font: inherit;
color: #fff;
cursor: pointer;
@ -123,38 +115,44 @@ body {
margin-top: -1px;
padding: 10px;
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;
border-color: #f1d33b;
position: relative;
}
&[disabled] {
.popup > div > section button[disabled] {
opacity: .5;
filter: saturate(0);
pointer-events: none;
}
& > div {
.popup > div > section button > div {
flex-grow: 1;
}
& > div > em {
.popup > div > section button > div > em {
opacity: .8;
display: block;
}
&.grey-out-unless-hovered {
&:not(:hover) {
.popup > div > section button.grey-out-unless-hovered:not(:hover) {
opacity: .6;
}
& img {
.popup > div > section button.grey-out-unless-hovered:not(:hover) img {
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 {
@ -168,8 +166,9 @@ body {
top: 0;
right: 0;
overflow: hidden;
}
&:before {
.popup button.close-modale:before {
content: "+";
font-size: 80px;
display: inline-block;
@ -179,11 +178,10 @@ body {
transform: translate(-50%, -50%)rotate(45deg);
}
&:hover {
.popup button.close-modale:hover {
background: #000;
font-weight: bold;
}
}
.popup .textAfterButtons {
color: #ffffff94;
@ -238,7 +236,7 @@ body {
margin: 40px;
}
#level-recording-container img, #level-recording-container video {
#level-recording-container video {
max-width: 100%;
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', ()=>{
@ -40,3 +41,9 @@ describe('sumOfKeys', ()=>{
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 {
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
}
export const makeEmptyPerksMap = (upgrades:Upgrade[]) => {
export const makeEmptyPerksMap = (upgrades: { id:PerkId }[]) => {
const p = {} as any;
upgrades.forEach((u) => (p[u.id] = 0));
return p as PerksMap;

View file

@ -1,15 +1,5 @@
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";
const backgrounds = _backgrounds as string[];
@ -22,3 +12,14 @@ export function getLevelBackground(level:RawLevel){
}
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."
/>
<style>
@import "style.css";
@import "game.less";
</style>
<link
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;
}