html {
font-size: 16px;
height: 100%;
}
body {
height: 100%;
margin: 0;
display: flex;
flex-direction: column;
font-family: Ubuntu, Source Sans Pro, DejaVu Sans, sans-serif;
line-height: 1.33;
background: #080808;
background-image: url(background.svg);
background-size: 10%;
color: #ececec;
--panel-bg-color: hsl(225, 10%, 20%);
--button-bg-color: hsl(225, 10%, 25%);
--button-bg-hover-color: hsl(225, 15%, 30%);
}
/* Generic element styling */
main[hidden] {
display: none !important;
}
input[type=radio],
input[type=checkbox],
input[type=range] {
margin: 0.125em;
vertical-align: middle;
}
button {
font-size: inherit;
padding: 0.25em 0.5em;
font-family: inherit;
color: white;
background: var(--button-bg-color);
border: 1px solid hsl(225, 10%, 15%);
box-shadow:
inset 0 0 2px 1px hsl(225, 10%, 33%),
0 1px 0 hsl(225, 10%, 10%);
border-radius: 0.25em;
text-transform: lowercase;
cursor: pointer;
}
button:hover {
background: var(--button-bg-hover-color);
}
button:active {
box-shadow:
inset 0 0 2px 1px hsl(225, 10%, 33%);
transform: translateY(1px);
}
button:disabled {
color: #606060;
background: #202020;
box-shadow:
inset 0 0 2px 1px hsl(225, 0%, 10%),
0 1px 0 hsl(225, 10%, 10%);
cursor: auto;
}
button.button-big {
display: block;
width: 100%;
margin: 0.5em 0;
padding: 1em;
}
h1, h2, h3, h4, h5, h6 {
font-weight: normal;
margin: 0;
}
ul, ol {
margin: 0;
padding: 0;
list-style: none;
}
ul.normal-list {
margin-left: 1em;
list-style: disc;
}
ol.normal-list {
margin-left: 1.5em;
list-style: decimal;
}
p {
margin: 0.5em 0;
}
p:first-child {
margin-top: 0;
}
p:last-child {
margin-bottom: 0;
}
pre {
white-space: pre-wrap;
}
code {
color: #c0c0e0;
}
a {
color: #c0c0c0;
}
a:link,
a:visited {
text-decoration: underline dotted;
}
a:link {
color: hsl(225, 50%, 60%);
}
a:visited {
color: hsl(300, 50%, 60%);
}
a:link:hover,
a:visited:hover {
text-decoration: underline;
}
a:active {
color: hsl(0, 50%, 60%);
}
svg.svg-icon {
width: 1em;
height: 1em;
vertical-align: middle;
stroke: none;
fill: currentColor;
}
/* Overlay styling */
.overlay {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #fff4;
}
.dialog {
display: flex;
flex-direction: column;
min-width: 33%;
max-width: 75%;
max-height: 75%;
border: 1px solid black;
color: black;
background: #f4f4f4;
box-shadow: 0 1px 3px #000c;
}
.dialog > header {
padding: 0.5em;
line-height: 1;
background: hsl(225, 20%, 40%);
color: white;
}
.dialog > header h1 {
font-size: 1em;
}
.dialog > footer {
display: flex;
justify-content: flex-end;
gap: 0.5em;
padding: 0.5em;
background: #d0d0d0;
}
.dialog > header:empty,
.dialog > footer:empty {
display: none;
}
.dialog > section {
overflow: auto;
padding: 1em;
}
.dialog pre.error {
color: #400000;
background: #f0d0d0;
padding: 0.5em 1em;
}
/* Individual overlays */
table.level-browser {
width: 100%;
/* for some reason the table ignores the bottom padding when it overflows */
margin-bottom: 1em;
line-height: 1.25;
border-spacing: 0;
}
table.level-browser thead {
position: sticky;
top: -1em; /* counteract padding so cells don't appear above us */
background: #f4f4f4; /* match dialog background */
}
table.level-browser thead tr th {
border-bottom: 2px solid hsl(225, 20%, 60%);
}
table.level-browser td {
padding: 0.25em;
}
table.level-browser td.-number {
color: #404040;
text-align: right;
}
table.level-browser td.-time {
text-align: right;
}
table.level-browser td.-score {
text-align: right;
}
table.level-browser tr.--unvisited {
color: #606060;
font-style: italic;
}
table.level-browser tr.--error {
color: #600000;
font-style: italic;
}
table.level-browser tbody tr {
cursor: pointer;
}
table.level-browser tbody tr:hover {
background: hsl(225, 60%, 85%);
}
table.level-browser tbody tr:nth-child(10n) td {
border-bottom: 2px solid hsl(225, 20%, 80%);
}
/* Options dialog */
.dialog-options {
height: 60%;
width: 75%;
}
.dialog-options > section {
flex: 1;
}
nav.tabstrip {
display: flex;
border-bottom: 1px solid #d0d0d0;
}
nav.tabstrip > a {
margin: 0 0.5em;
padding: 0.5em 1em;
color: inherit;
text-decoration: none;
border-top-left-radius: 0.5em;
border-top-right-radius: 0.5em;
}
nav.tabstrip > a:hover {
background: #e8e8e8;
}
nav.tabstrip > a.--selected {
background: #d0d0d0;
}
.dialog section.tabblock {
display: none;
overflow: auto;
margin: 0.25em 0.5em;
}
.dialog section.tabblock.--selected {
display: initial;
}
label.option {
display: flex;
align-items: center;
padding: 0.25em;
}
label.option:hover {
outline: 2px solid #d0d0d0;
outline-radius: 2px;
}
label.option .option-label {
flex: 1;
}
.option-help {
display: none;
background: #e8e8e8;
padding: 0.5em 0.75em;
border-radius: 0.5em;
}
.option-help.--visible {
/* TODO */
}
@media (max-width: 800px) {
.dialog {
max-width: 90%;
max-height: 90%;
}
}
/* Bits and pieces */
img.compat-icon {
margin: 0 0.25em 0.125em;
vertical-align: middle;
}
.compat-lynx,
.compat-ms {
font-size: 0.75em;
display: inline-block;
margin: 0 0.25em;
padding: 0.25em;
line-height: 1;
vertical-align: middle;
color: white;
background: gray;
border-radius: 0.25em;
}
/**************************************************************************************************/
/* Main page structure */
body > header {
display: flex;
align-items: center;
gap: 0.5em;
padding: 0.5em;
line-height: 1.125;
}
body > header h1 {
font-size: 1.66em;
}
body > header h2 {
font-size: 1.33em;
}
body > header h3 {
font-size: 1.75em;
}
body > header > nav {
flex: 1;
display: flex;
justify-content: flex-end;
gap: 0.5em;
}
body > header button {
font-size: 0.75em;
}
body[data-mode=splash] #header-pack,
body[data-mode=splash] #header-level {
display: none;
}
body[data-mode=editor] #player-edit,
body[data-mode=player] #editor-play {
display: none;
}
#header-main {
order: 3;
color: #606060;
}
@media (max-width: 800px) {
body > header {
padding: 0.25em;
}
/* All these headings are way too big on phones */
body > header h1 {
font-size: 1.25em;
}
body > header h2 {
font-size: 1.125em;
}
body > header h3 {
font-size: 1.0625em;
}
body > header p {
/* "a game by eevee" takes up too much space :( */
display: none;
}
}
/**************************************************************************************************/
/* Splash (intro part) */
#splash {
display: grid;
grid:
"header header header"
"intro intro intro"
"stock upload yours"
/ 1fr 1fr 1fr
;
gap: 1em;
position: relative;
padding: 1em 10%;
margin: auto;
overflow: auto;
}
#splash > .drag-overlay {
display: none;
justify-content: center;
align-items: center;
font-size: 10vmin;
position: absolute;
top: 0;
bottom: 0;
left: 1rem;
right: 1rem;
background: #fff2;
border: 0.25rem dashed white;
border-radius: 1rem;
text-shadow: 0 1px 5px black;
text-align: center;
}
#splash.--drag-hover > .drag-overlay {
display: flex;
}
#splash > header {
grid-area: header;
text-align: center;
}
#splash > header img {
display: block;
margin: auto;
}
#splash > header h1 {
font-size: 3em;
}
#splash h2 {
border-bottom: 1px solid #404040;
color: #909090;
text-shadow: 0 1px #0004;
}
#splash > section {
padding: 1em;
background: var(--panel-bg-color);
box-shadow: 0 0.25em 1em black;
}
#splash > #splash-intro {
grid-area: intro;
font-size: 18px;
}
#splash > #splash-stock-levels {
grid-area: stock;
}
#splash > #splash-upload-levels {
grid-area: upload;
}
#splash-upload-file,
#splash-upload-dir {
/* Hide the file upload control, which is ugly */
display: none;
}
#splash > #splash-your-levels {
grid-area: yours;
}
@media (max-width: 800px) {
#splash {
/* Grid layout doesn't fit, just stack everything */
display: flex;
flex-direction: column;
/* 10% padding is way way too much */
padding: 1em;
}
/* Shrink logo and title */
#splash > header img {
width: 48px;
}
#splash > header h1 {
font-size: 2em;
}
}
button.level-pack-button {
display: grid;
grid:
"title score"
"desc desc"
/ 1fr min-content
;
padding: 0.5em;
text-align: left;
}
button.level-pack-button h3 {
grid-area: title;
}
button.level-pack-button .-score {
grid-area: score;
font-style: italic;
color: #c0c0c0;
text-align: right;
}
button.level-pack-button p {
grid-area: desc;
font-size: 0.833em;
font-style: italic;
color: #c0c0c0;
}
/**************************************************************************************************/
/* Player */
#player {
flex: 0;
display: flex;
flex-direction: column;
justify-content: stretch;
gap: 0.5em;
margin: auto; /* center in both directions baby */
image-rendering: crisp-edges;
image-rendering: pixelated;
--tile-width: 32px;
--tile-height: 32px;
--scale: 1;
}
#player > .-main-area {
isolation: isolate;
display: grid;
align-items: center;
grid:
"level chips" min-content
"level time" min-content
"level bonus" min-content
"level message" 1fr
"level inventory" min-content
/* Need explicit min-content to force the hint to wrap */
/ min-content min-content
;
column-gap: 2em;
row-gap: 0.5em;
padding: 1em;
background: hsl(225, 10%, 20%);
box-shadow: 0 0.25em 1em black;
}
#player > .controls {
order: -1;
}
.level {
grid-area: level;
position: relative;
border: 0.125em solid black;
}
.level canvas {
display: block;
width: calc(var(--viewport-width) * var(--tile-width) * var(--scale));
--viewport-width: 9;
--viewport-height: 9;
}
#player .overlay-message {
grid-area: level;
place-self: stretch;
display: grid;
grid-template-rows: 2fr 6fr 2fr 1fr;
justify-content: center;
align-items: center;
z-index: 2;
font-size: calc(0.5 * var(--tile-width) * var(--scale));
padding: 2%;
background: #0009;
color: white;
text-align: center;
text-shadow: 0 2px 1px black;
}
#player .overlay-message p {
margin: 0;
}
#player .overlay-message .-top {
font-size: 1.5em;
}
#player .overlay-message .-middle {
}
#player .overlay-message .-bottom {
}
#player .overlay-message .-keyhint {
align-self: end;
font-size: 0.5em;
color: #c0c0c0;
}
#player .overlay-message[data-reason=""] {
display: none;
}
#player .overlay-message[data-reason=failure] {
box-shadow: inset 0 0 calc(4 * var(--tile-width)) var(--tile-width) black;
}
#player .overlay-message[data-reason=success] {
background: hsla(225, 50%, 25%, 0.5);
box-shadow: inset 0 0 calc(4 * var(--tile-width)) hsl(225, 50%, 25%);
}
dl.score-chart {
display: grid;
grid-auto-columns: 1fr 1fr;
margin: auto;
font-weight: normal;
}
dl.score-chart dt {
grid-column: 1;
text-align: left;
}
dl.score-chart dd {
grid-column: 2;
margin: 0;
text-align: right;
}
dl.score-chart .-sum {
margin-bottom: 0.5em;
border-top: 1px solid white;
color: hsl(40, 75%, 80%);
}
.chips {
grid-area: chips;
}
.time {
grid-area: time;
}
.bonus {
grid-area: bonus;
}
.chips,
.time,
.bonus {
font-size: calc(var(--tile-height) * var(--scale) / 3);
display: flex;
align-items: center;
}
.chips h3,
.time h3,
.bonus h3 {
flex: 1;
font-size: 1.25em;
line-height: 1;
color: hsl(225, 20%, 90%);
}
.chips output,
.time output,
.bonus output {
flex: 1;
font-size: 2em;
min-width: 2em;
min-height: 1em;
line-height: 1;
text-align: right;
font-family: monospace;
color: hsl(225, 20%, 60%);
}
.chips output.--done {
color: hsl(225, 10%, 30%);
}
.time output.--warning {
color: hsl(345, 60%, 60%);
}
.time output.--danger {
color: hsl(330, 60%, 60%);
/* TODO this can get out of sync and keeps going at 0, but is a neat idea */
/* animation: time-pulse 1s linear infinite; */
}
@keyframes time-pulse {
0% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
.time output.--frozen {
color: hsl(225, 10%, 30%);
}
#player .bonus {
visibility: hidden;
display: none;
}
#player.--bonus-visible .bonus {
visibility: initial;
display: initial;
}
.message {
grid-area: message;
align-self: stretch;
font-size: calc(var(--tile-height) * var(--scale) / 3);
padding: 0.25em 0.5em;
font-family: serif;
font-style: italic;
color: hsl(45, 100%, 60%);
background: #080808;
border: 1px inset #202020;
/* FIXME find a way to enforce that the message never makes the grid get bigger */
overflow: auto;
}
.message:empty {
display: none;
}
#player .inventory {
grid-area: inventory;
justify-self: center;
display: grid;
grid: auto-flow calc(var(--tile-height) * var(--scale)) / repeat(4, calc(var(--tile-width) * var(--scale)));
background-size: calc(var(--tile-width) * var(--scale)) calc(var(--tile-height) * var(--scale));
width: calc(4 * var(--tile-width) * var(--scale));
height: calc(2 * var(--tile-height) * var(--scale));
}
#player .inventory img {
width: calc(var(--tile-width) * var(--scale));
}
#player .inventory .--hidden {
visibility: hidden;
pointer-events: none;
}
#player .inventory > span {
position: relative;
}
#player .inventory .-count {
font-size: calc(0.25 * var(--tile-height) * var(--scale));
position: absolute;
top: 0;
right: 0;
padding: 0.25em 0.5em;
margin: 0.25em; /* 2px, for a 32px tileset */
line-height: 1;
border-radius: 0.25em;
background: #0009;
color: white;
}
#player-music {
grid-area: music;
display: flex;
gap: 1em;
margin: 0 1em;
text-transform: lowercase;
color: #909090;
}
#player-music #player-music-left {
/* allow me to wrap if need be */
flex: 1 0 0px;
}
#player-music #player-music-right {
text-align: right;
}
#player-music #player-music-volume {
width: 8em;
}
#player .controls {
grid-area: controls;
display: flex;
}
.play-controls,
.demo-controls {
display: flex;
align-items: center;
gap: 0.25em;
}
.play-controls {
align-self: start;
}
.demo-controls {
display: none;
flex: 1;
justify-content: flex-end;
}
main.--has-demo .demo-controls {
display: flex;
}
/* Debug stuff */
#player-debug {
display: none;
background-image: repeating-linear-gradient(135deg,
hsl(0, 0%, 12.5%) 0em,
hsl(0, 0%, 12.5%) 1em,
hsl(45, 25%, 12.5%) 1em,
hsl(45, 25%, 12.5%) 2em);
}
.input {
display: grid;
display: none;
grid:
"drop up cycle" 1.5em
"left swap right" 1.5em
". down . " 1.5em
/ 1.5em 1.5em 1.5em
;
gap: 0.5em;
}
.input-action {
padding: 0.25em;
line-height: 1;
color: #fff4;
background: #202020;
}
.input-action[data-action=up] { grid-area: up; }
.input-action[data-action=down] { grid-area: down; }
.input-action[data-action=left] { grid-area: left; }
.input-action[data-action=right] { grid-area: right; }
.input-action[data-action=swap] { grid-area: swap; }
.input-action[data-action=cycle] { grid-area: cycle; }
.input-action[data-action=drop] { grid-area: drop; }
.input-action.--pressed {
color: white;
background: hsl(225, 75%, 25%);
}
@media (max-width: 800px) {
#player {
/* sentinel for js */
--is-portrait: 1;
/* The play area isn't necessarily the biggest thing any more, and it's ugly when stretched */
align-items: center;
}
#player > .-main-area {
/* Rearrange the grid to be vertical */
grid:
"level level"
"chips inventory" calc((var(--tile-height) * var(--scale) * 2 - 1em) / 3)
"time inventory" calc((var(--tile-height) * var(--scale) * 2 - 1em) / 3)
"bonus inventory" calc((var(--tile-height) * var(--scale) * 2 - 1em) / 3)
/* FIXME ideally the first column would be 1fr so the hearts/time have space, but that
* allows hints to grow to the entire width of the window, which incredibly sucks. i
* don't know how to get around this except by giving the grid a fixed width, which i
* guess wouldn't be that hard */
/ min-content min-content
;
row-gap: 0.5em;
column-gap: 1em;
padding: 0.5em;
}
#player .inventory {
/* stick me in the center right */
place-self: center end;
}
#player .message {
/* Overlay hints on the inventory area */
grid-row: chips / bonus;
grid-column: level;
z-index: 1;
font-size: calc(var(--tile-height) * var(--scale) / 2.5);
}
#player .controls .keyhint {
/* Hide key hints, they take up surprisingly a lot of space */
display: none;
}
#player-music {
/* Stack the title/artist on the volume, since they don't fit well side by side */
font-size: 0.875em;
}
}
/**************************************************************************************************/
/* Editor */
#editor {
flex: 1 1 auto;
display: grid;
grid:
"controls controls" min-content
"palette level" 1fr
/ calc((32px + 4px) * 8) 1fr
;
gap: 0.5em;
min-height: 0;
margin: auto 1em;
}
#editor .level {
grid-area: level;
position: relative;
overflow: auto;
/* Padding and background make it easier to tell when we're at the edge of the map */
/* TODO padding should be half a cell, and svg should respect it too */
/* padding: 1em; */
background: #202020;
}
/* SVG overlays */
#editor svg.level-editor-overlay {
position: absolute;
top: 0;
left: 0;
/* FIXME get real size */
width: 1024px;
height: 1024px;
/* allow clicks to go through us! */
pointer-events: none;
/* default svg properties */
stroke-width: 0.0625;
fill: none;
}
#editor .level-editor-overlay rect.overlay-cxn {
stroke: red;
}
#editor .level-editor-overlay line.overlay-cxn {
stroke: red;
}
#editor .level-editor-overlay rect.overlay-camera {
stroke: #808080;
fill: #80808040;
pointer-events: auto;
}
#editor .level-editor-overlay text {
/* Each cell is one "pixel", so text needs to be real small */
font-size: 1px;
}
#editor .level-editor-overlay text.overlay-edit-tip {
stroke: none;
fill: black;
}
#editor .controls {
grid-area: controls;
display: grid;
grid:
"toolbar layer direction menu" auto
"hint hint hint hint" 1.5em
;
}
#editor .controls #editor-toolbar {
grid-area: toolbar;
}
#editor .controls .-buttons {
grid-area: menu;
}
#editor .controls #editor-tool-help {
grid-area: hint;
}
.icon-button-set {
display: flex;
flex-wrap: wrap;
}
.icon-button-set button {
width: auto;
height: auto;
padding: 0;
margin: 0;
line-height: 1;
background: url(icons/tool-bg-unselected.png) no-repeat;
border: none;
border-radius: 0;
}
.icon-button-set button.-selected {
background-image: url(icons/tool-bg-selected.png);
}
.icon-button-set button img {
display: block;
}
#editor .palette {
grid-area: palette;
min-width: 20vw;
padding-right: 0.25em; /* in case of scrollbar */
overflow-y: auto;
}
#editor .palette h2 {
font-size: 1em;
margin-top: 1em;
border-bottom: 1px solid currentColor;
color: #909090;
}
#editor .palette h2:first-child {
margin-top: 0;
}
#editor .palette section {
display: grid;
grid: auto-flow 32px / repeat(auto-fit, 32px);
gap: 4px;
}
.palette-entry {
margin: 0.25em;
}
.palette-entry.--selected {
box-shadow: 0 0 0 1px black, 0 0 0 2px white;
}