aboutsummaryrefslogtreecommitdiff
path: root/styles
diff options
context:
space:
mode:
Diffstat (limited to 'styles')
-rw-r--r--styles/dark.css22
-rw-r--r--styles/disk.css4
-rw-r--r--styles/game.css76
-rw-r--r--styles/gameSettings.css77
-rw-r--r--styles/global.css90
-rw-r--r--styles/index.css71
-rw-r--r--styles/loginregister.css13
-rw-r--r--styles/navbar.css36
-rw-r--r--styles/notifications.css52
-rw-r--r--styles/readme.md103
-rw-r--r--styles/recentGames.css21
-rw-r--r--styles/search.css11
-rw-r--r--styles/settings.css8
-rw-r--r--styles/toast.css66
-rw-r--r--styles/ui.css118
-rw-r--r--styles/user.css46
-rw-r--r--styles/utility.css102
17 files changed, 778 insertions, 138 deletions
diff --git a/styles/dark.css b/styles/dark.css
index 681116f..b44171b 100644
--- a/styles/dark.css
+++ b/styles/dark.css
@@ -1,9 +1,19 @@
html.dark {
- --text: #FCFFFD;
- --text-alt: var(--text);
+ --background: var(--gray-900);
+ --foreground: #FFFFF3;
- --page-background: #11171a;
- --background: #222d33;
- --background-alt: #344047;
-}
+ --accent: #AD34F3;
+
+ --error: #FF4365;
+ --disk-b-alt: #F49BA1;
+ --confirm: #00D9C0;
+ --disk-a-alt: #86F3F3;
+
+ --gray-100: #CED2DC;
+ --gray-200: #A9AFC0;
+ --gray-300: #757D92;
+ --gray-700: #293140;
+ --gray-800: #1F242D;
+ --gray-900: #141619;
+}
diff --git a/styles/disk.css b/styles/disk.css
index d858335..2646fed 100644
--- a/styles/disk.css
+++ b/styles/disk.css
@@ -1,3 +1,3 @@
.voerBord .disk.state-0 { background-color: var(--page-background); }
-.voerBord .disk.state-1 { background-color: var(--disk-a); }
-.voerBord .disk.state-2 { background-color: var(--disk-b); }
+.voerBord .disk.state-1 { background-color: var(--confirm); }
+.voerBord .disk.state-2 { background-color: var(--error); }
diff --git a/styles/game.css b/styles/game.css
new file mode 100644
index 0000000..d482f9b
--- /dev/null
+++ b/styles/game.css
@@ -0,0 +1,76 @@
+.outcomeDialog .inner > * {
+ margin-top: var(--spacing-medium);
+}
+
+.outcomeDialog .inner .button {
+ float: unset;
+ padding: var(--spacing-medium) var(--spacing-large);
+}
+
+.newGameDialog .inviteButton { height: 160px; }
+
+.newGameDialog .inviteButton.random { background-color: var(--confirm); }
+
+.newGameDialog .inviteButton.link { background-color: var(--error); }
+
+.newGameDialog .inviteButton .icon {
+ top: var(--spacing-medium);
+ font-size: 100px;
+}
+
+.newGameDialog .inviteButton .label { bottom: var(--spacing-medium); }
+
+.newGameDialog > div { margin-top: var(--spacing-large); }
+
+.voerBord { border-spacing: var(--spacing-small); }
+
+.voerBord .cell.inner { border: 2px solid var(--gray-800); }
+
+.voerBord .square { margin-top: 100%; }
+
+.voerBord.active .cell.inner { cursor: pointer; }
+
+.voerBord .disk {
+ border-radius: 999999999px;
+ margin: 3px;
+}
+
+.gameBar .gameBarButton {
+ margin: 0;
+ margin-left: var(--spacing-small);
+}
+
+.gameBar .move {
+ width: 32px;
+ height: 32px;
+ border-radius: 16px;
+
+ margin: 8px;
+}
+
+.gameBar .move.move-a { background-color: var(--error); }
+.gameBar .move.move-b { background-color: var(--confirm); }
+
+.gameBar .timer span {
+ margin: 0 4px;
+ font-size: 20px;
+}
+
+.gameBar .score {
+ font-size: 20px;
+ z-index: 1;
+}
+
+.gameBar .score.winning { color: var(--disk-a-alt); }
+.gameBar .score.losing { color: var(--disk-b-alt); }
+
+.voerGame {
+ max-width: 100vh;
+ margin: 0 auto;
+}
+
+html.dark .newGameDialog .inviteButton { background-color: var(--gray-800); }
+html.dark .newGameDialog .inviteButton.random .icon { color: var(--confirm); }
+html.dark .newGameDialog .inviteButton.link .icon { color: var(--error); }
+html.dark .newGameDialog .searchBar .input { background-color: var(--gray-800); }
+
diff --git a/styles/gameSettings.css b/styles/gameSettings.css
new file mode 100644
index 0000000..3c45fb4
--- /dev/null
+++ b/styles/gameSettings.css
@@ -0,0 +1,77 @@
+.editGameSettings {
+ height: 80px;
+ overflow: visible;
+ z-index: 1;
+}
+
+.editGameSettings .currentRules {
+ top: 50%;
+ transform: translateY(-50%);
+}
+
+.editGameSettings > .button {
+ width: 150px;
+ top: 50%;
+ transform: translateY(-50%);
+ text-align: left;
+}
+
+.editGameSettings > .button .icon {
+ font-size: 48px;
+}
+
+.editGameSettings > .button .text {
+ right: var(--spacing-medium);
+ width: 85px;
+ top: 50%;
+ vertical-align: middle;
+ transform: translateY(-50%);
+}
+
+.editGameSettings .editableRules {
+ margin: var(--spacing-large) 0;
+ max-height: 500px;
+ overflow-y: scroll;
+}
+
+.editGameSettings .editableRules .editableRulesSection {
+ width: 100%;
+ background-color: var(--gray-700);
+ margin: 0;
+ margin-bottom: var(--spacing-medium);
+}
+html.dark .editGameSettings .editableRules .editableRulesSection {
+ background-color: var(--gray-800);
+}
+.editGameSettings .editableRules .editableRulesSection:last-child {
+ margin-bottom: 0;
+}
+
+.editGameSettings .button { line-height: normal; }
+
+.editGameSettings .checkboxWrapper { margin: -3px; }
+
+.editGameSettings .editableRules .editableRulesSection .timeControls {
+ margin: var(--spacing-medium);
+ margin-left: 0;
+ margin-right: 0;
+}
+
+.editGameSettings .editableRules .editableRulesSection .timeControls input {
+ background-color: var(--background);
+ width: calc(100% - 2 * var(--spacing-medium));
+}
+
+.editGameSettings .editableRules .gamerule {
+ margin-top: var(--spacing-medium);
+}
+
+.editGameSettings .editableRules .gamerule h1 {
+ color: var(--accent);
+ font-size: 2.5rem;
+}
+
+#timelimit .valignsup {
+ margin-left: var(--spacing-small);
+}
+
diff --git a/styles/global.css b/styles/global.css
index 1e87524..79aa026 100644
--- a/styles/global.css
+++ b/styles/global.css
@@ -1,15 +1,36 @@
-html {
- --text: #FCFFFD;
- --page-background: var(--text);
-
- --background: #5D737E;
- --text-alt: var(--background);
- --background-alt: #81949E;
-
- --disk-a: #E16D82;
- --disk-a-text: #FDC0C4;
- --disk-b: #71D9CC;
- --disk-b-text: #C0FDEB;
+:root {
+ --background: var(--gray-900);
+ --foreground: var(--gray-100);
+
+ --accent: #7E3AA6;
+
+ --error: #A63A4D;
+ --disk-b-alt: #582D35;
+
+ --confirm: #3AA699;
+ --disk-a-alt: #244743;
+
+ /* shade */
+ --gray-100: #141619;
+ --gray-200: #1F242D;
+ --gray-300: #293140;
+ --gray-600: #757D92;
+ --gray-700: #A9AFC0;
+ --gray-800: #CED2DC;
+ --gray-900: #E3E6EE;
+
+ /* box-shadow */
+ --drop-level-2: 0px 8px 32px 0px rgba(0, 0, 0, 0.3);
+ --drop-level-1: 0px 8px 12px -4px rgba(0, 0, 0, 0.15);
+
+ /* border-radius */
+ --tight-corner: 6px;
+ --loose-corner: 8px;
+
+ /* margin/padding */
+ --spacing-small: 6px;
+ --spacing-medium: 12px;
+ --spacing-large: 24px;
}
/* default margin */
@@ -18,6 +39,9 @@ html, body {
padding: 0;
}
+/* section font size */
+h1 { font-size: 25px; }
+
/* subsection font size */
h2 { font-size: 20px; }
@@ -27,17 +51,17 @@ h3 {
padding-bottom: 6px;
}
-/* global font zize */
-p, b, i, span, td, th { font-size: 15px; }
-
/* navbar fix */
body { padding-left: 48px; }
/* font */
-html { font-family: "Inter"; }
+html { font-family: "Inter", sans-serif; }
-/* background color */
-html, body { background-color: var(--page-background); }
+/* color */
+:root, html, body {
+ background-color: var(--background);
+ color: var(--foreground);
+}
/* link fix */
a {
@@ -59,14 +83,20 @@ h1, h2, h3, p, b, i, span, td, th {
table { table-layout: fixed; }
/* table styles */
-td, th {
- padding: 4px;
- font-size: 15px;
+td, th { padding: 4px; }
+
+input {
+ color: var(--foreground);
+ background-color: transparent;
+ font-family: inherit;
+ border: 0;
+ font-size: 1rem; /* why? */
}
input::placeholder {
font-style: italic;
- opacity: .8;
+ opacity: 1;
+ color: var(--gray-600);
}
/* remove chrome's ugly :focus outline */
@@ -78,19 +108,3 @@ input::placeholder {
/* material-ui default state */
svg.MuiSvgIcon-root { transition: none !important; }
-input::placeholder { opacity: .75; }
-input.dark::placeholder { color: var(--text); }
-input.light::placeholder { color: var(--text-alt); }
-
-/* editable field status */
-*[contenteditable] { border-color: var(--background-alt); }
-*[contenteditable="true"]:focus { border-color: var(--disk-a); }
-*[contenteditable="true"] {
- background-color: var(--page-background);
- color: var(--text-alt);
- padding: 6px;
- border-radius: 6px;
- border-style: solid;
- border-width: 2px;
-}
-
diff --git a/styles/index.css b/styles/index.css
new file mode 100644
index 0000000..fcda8e0
--- /dev/null
+++ b/styles/index.css
@@ -0,0 +1,71 @@
+.topbar * {
+ --squareSize: 90px;
+}
+
+.loginOrRegisterBox {
+ height: calc(var(--squareSize) + 2 * var(--spacing-large));
+ width: 100%;
+ max-width: calc(100% - var(--squareSize) - var(--spacing-medium) * 2 - var(--spacing-large) * 2);
+}
+
+.loginOrRegisterBox .inner {
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+
+.loginOrRegisterBox .inner .registerMessage {
+ user-select: none;
+ display: inline-block;
+ margin: 0 auto;
+ min-width: 240px;
+ max-width: 350px;
+ text-align: center;
+}
+
+.loginOrRegisterBox .inner .button.register {
+ background-color: var(--gray-700);
+ color: var(--foreground);
+}
+
+.gamemode .icon {
+ font-size: 64px;
+ font-size: 64px;
+ height: 64px;
+ display: inline-block;
+ position: absolute;
+ top: var(--spacing-large);
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.gamemode .text {
+ white-space: nowrap;
+ display: inline-block;
+ position: absolute;
+ bottom: var(--spacing-large);
+ left: 50%;
+ transform: translateX(-50%);
+ user-select: none;
+ font-weight: 500;
+}
+
+.topbar .gamemode {
+ --size: calc(var(--squareSize) + 2 * var(--spacing-large));
+ width: var(--size);
+ height: var(--size);
+}
+
+.topbar .profile .info {
+ left: calc(90px + 12px);
+ width: calc(100% - 90px - 12px);
+ height: 100%;
+}
+
+.topbar .profile .info .username { max-width: 178px; }
+.topbar .profile .info .score { margin-top: var(--spacing-small); }
+
+.topbar .profile .info .games .divider {
+ margin: 0 3px;
+}
+
diff --git a/styles/loginregister.css b/styles/loginregister.css
new file mode 100644
index 0000000..854aeb2
--- /dev/null
+++ b/styles/loginregister.css
@@ -0,0 +1,13 @@
+.centeredForm {
+ top: 50%;
+ transform: translateY(-50%);
+ margin: 0 auto;
+}
+
+.centeredForm .input {
+ margin-bottom: var(--spacing-medium);
+}
+
+.centeredForm .input:nth-last-of-type(2) {
+ margin-bottom: var(--spacing-large);
+}
diff --git a/styles/navbar.css b/styles/navbar.css
new file mode 100644
index 0000000..baf9fd6
--- /dev/null
+++ b/styles/navbar.css
@@ -0,0 +1,36 @@
+.navbar .item {
+ margin: var(--spacing-medium);
+ margin-bottom: calc(var(--spacing-medium) + 4px);
+ display: block;
+}
+
+.navbar .bottomArea {
+ position: absolute;
+ bottom: -4px;
+ left: 0;
+}
+
+.navbar {
+ width: 48px;
+ line-height: 0;
+ overflow: visible;
+ white-space: nowrap;
+ z-index: 2;
+}
+
+.navbar .bottomArea .item.notifications {
+ overflow: visible;
+}
+
+.navbar .bottomArea .item.notifications > .iconWrapper {
+ cursor: pointer;
+}
+
+.notifications .notificationDot {
+ background-color: var(--accent);
+ width: 8px;
+ height: 8px;
+ border-radius: 4px;
+ top: 2px;
+ right: 2px;
+}
diff --git a/styles/notifications.css b/styles/notifications.css
new file mode 100644
index 0000000..db1598d
--- /dev/null
+++ b/styles/notifications.css
@@ -0,0 +1,52 @@
+/* a tags are here because of css specificity */
+a.notificationsArea {
+ left: calc(48px + var(--spacing-medium));
+ top: 92px;
+ transform: translateY(-100%);
+ text-align: left;
+ width: 400px;
+ height: 450px;
+}
+
+a.notificationsArea .tuitje {
+ left: var(--spacing-medium);
+ bottom: 86px;
+ transform: translate(-100%, 100%) rotate(90deg);
+ fill: var(--gray-700);
+}
+
+.notificationsArea .title {
+ margin-bottom: var(--spacing-large);
+}
+
+.notificationsArea .inner {
+ overflow-y: scroll;
+ white-space: normal;
+ height: calc(450px - 4 * var(--spacing-large));
+}
+
+.notificationsArea .acceptable {
+ margin: 0;
+ margin-bottom: var(--spacing-medium);
+}
+
+.notificationsArea .acceptable:last-child {
+ margin-bottom: 0;
+}
+
+.notificationsArea .buttons .button {
+ margin: 0;
+}
+
+.notificationsArea .buttons {
+ margin-top: var(--spacing-medium);
+}
+
+.notificationsArea .noMsgsWrapper h1 {
+ white-space: nowrap;
+ user-select: none;
+}
+
+.notificationsArea .acceptable .userInfo {
+ margin-left: var(--spacing-small);
+}
diff --git a/styles/readme.md b/styles/readme.md
index 1d1e96f..58e6db0 100644
--- a/styles/readme.md
+++ b/styles/readme.md
@@ -1,103 +1,22 @@
-# Styles
+# styles
-This is the folder where all website css should be. Currently most of the css is
-done using inline styles in the .tsx files, but they're being moved to seperate
-.css files in here. A redesign has also been in the making and we now have a
-style guide to follow while writing the website:
+## dark
-## Dark
+![dark mode style guide](./styleguidedark.png)
-![Dark mode style guide](./styleguidedark.png)
+## light
-## Light
-
-![Light mode style guide](./styleguidelight.png)
+![light mode style guide](./styleguidelight.png)
The new designs can be found on
[Figma](https://www.figma.com/file/rTciVQApAe6cwrH1Prl5Wn/4-op-een-rij?node-id=0%3A1).
The Figma designs are in dark mode, but the website will (for now) still have
light mode as it's default theme.
-After the move to .css files is done I'll also start working on the new theming
-settings, and allow custom themes to be created with css that can easily be
-selected in the settings page instead of having to copy-paste color codes
-manually.
-
-## Show me the css
-
-These will eventually be moved to global.css when the move to .css files is
-done.
-
-```css
-:root {
- --background: #E3E6EE;
- --foreground: var(--gray-900);
-
- --accent: #7E3AA6;
-
- --error: #A63A4D;
- --disk-b-alt: #582D35;
-
- --confirm: #3AA699;
- --disk-a-alt: #244743;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --background: var(--gray-900);
- --foreground: #FFFFF3;
-
- --accent: #AD34F3;
-
- --error: #FF4365;
- --disk-b-alt: #F49BA1;
-
- --confirm: #00D9C0;
- --disk-a-alt: #86F3F3;
- }
-}
-
-:root {
- /* shade */
- --gray-100: #CED2DC;
- --gray-200: #A9AFC0;
- --gray-300: #757D92;
- --gray-700: #293140;
- --gray-800: #1F242D;
- --gray-900: #141619;
-
- /* box-shadow */
- --drop-level-2: 0px 8px 32px 0px rgba(0, 0, 0, 0.3);
- --drop-level-1: 0px 8px 12px -4px rgba(0, 0, 0, 0.15);
-
- /* border-radius */
- --tight-corner: 6px;
- --loose-corner: 8px;
-
- /* margin/padding */
- --spacing-small: 6px;
- --spacing-medium: 12px;
- --spacing-large: 24px;
-}
-
-html {
- font-family: "Inter", sans-serif;
- font-size: 14px;
-}
-
-/* headings */
-h1, h2, h3 { font-weight: 700; }
-h1 { font-size: 25px; }
-h2 { font-size: 20px; }
-
-.subtile {
- color: var(--gray-300);
- font-style: italic;
-}
+I'm now working on the new theming settings, and allow custom themes to be
+created with css that can easily be selected in the settings page instead of
+having to copy-paste color codes manually.
-/* no-select */
-.nosel {
- user-select: none;
- font-weight: 600;
-}
-```
+There are utility styles in ./utility.css. These are supposed to mimick
+something like tailwind css. Most styles from the style guide are also defined
+in ./global.css.
diff --git a/styles/recentGames.css b/styles/recentGames.css
new file mode 100644
index 0000000..c7150aa
--- /dev/null
+++ b/styles/recentGames.css
@@ -0,0 +1,21 @@
+table .leftAlignedColumn {
+ text-align: left;
+ padding-left: var(--spacing-medium);
+}
+
+table .rightAlignedColumn {
+ text-align: right;
+ padding-left: var(--spacing-medium);
+}
+
+table.recentGames {
+ margin-top: var(--spacing-medium);
+}
+
+table.recentGames a {
+ font-weight: 500;
+}
+
+div.recentGames .nogames {
+ margin: 32px 64px;
+}
diff --git a/styles/search.css b/styles/search.css
new file mode 100644
index 0000000..696294d
--- /dev/null
+++ b/styles/search.css
@@ -0,0 +1,11 @@
+.bigSearchBar .input {
+ width: calc(100% - 2 * var(--spacing-medium) - 48px);
+}
+
+.results .result .inner .userInfo {
+ left: calc(48px + var(--spacing-medium));
+}
+
+h1.noresults {
+ margin: 24px 32px;
+}
diff --git a/styles/settings.css b/styles/settings.css
new file mode 100644
index 0000000..bcce5b5
--- /dev/null
+++ b/styles/settings.css
@@ -0,0 +1,8 @@
+.section.logout .button {
+ float: unset;
+}
+
+.subsection {
+ margin-top: var(--spacing-large);
+ min-height: 40px;
+}
diff --git a/styles/toast.css b/styles/toast.css
new file mode 100644
index 0000000..2ecbcfb
--- /dev/null
+++ b/styles/toast.css
@@ -0,0 +1,66 @@
+.toast.error {
+ background-color: var(--error);
+}
+.toast.confirmation {
+ background-color: var(--confirm);
+}
+.toast.normal {
+ background-color: var(--gray-700);
+}
+
+.toast.confirmation,
+.toast.error {
+ color: var(--gray-900);
+}
+
+html.dark .toast.confirmation,
+html.dark .toast.error {
+ background-color: var(--gray-700);
+ color: var(--foreground);
+}
+html.dark .toast.error {
+ box-shadow: inset 0 0 0 2px var(--error);
+}
+html.dark .toast.confirmation {
+ box-shadow: inset 0 0 0 2px var(--confirm);
+}
+
+.toast {
+ margin-bottom: var(--spacing-medium);
+}
+
+#ToastArea {
+ white-space: nowrap;
+ bottom: var(--spacing-medium);
+ z-index: 1;
+ max-width: 600px;
+ width: calc(100% - 2 * 48px);
+ margin: 0 var(--spacing-large);
+}
+
+.toast .inner {
+ line-height: 0;
+ min-height: 24px;
+}
+
+.toast .inner.hasDescription {
+ min-height: 36px;
+}
+
+.toast .inner .icon,
+.toast .inner .content {
+ left: var(--spacing-medium);
+}
+
+.toast .inner.hasIcon .content {
+ left: calc(36px + var(--spacing-medium));
+}
+
+.toast .inner .content h2 {
+ font-size: 1rem;
+}
+
+.toast .inner .closeIcon {
+ right: var(--spacing-medium);
+ cursor: pointer;
+}
diff --git a/styles/ui.css b/styles/ui.css
new file mode 100644
index 0000000..2736a2c
--- /dev/null
+++ b/styles/ui.css
@@ -0,0 +1,118 @@
+.vierkant {
+ margin: var(--spacing-small);
+ display: inline-block;
+ position: relative;
+ box-sizing: border-box;
+}
+
+html.dark .button { color: var(--foreground); }
+.button {
+ background-color: var(--accent);
+ color: var(--gray-900);
+ text-align: center;
+ cursor: pointer;
+ position: relative;
+ text-decoration: none;
+ display: block;
+ user-select: none;
+ font-weight: 600;
+}
+
+.button.iconlabel,
+.button.colorpicker {
+ margin-left: var(--spacing-medium);
+}
+
+.button.colorpicker {
+ --color: var(--gray-100);
+ --background: #ff00ff;
+
+ background-color: var(--background);
+ color: var(--color);
+ border-width: 2px;
+ border-style: solid;
+ border-color: var(--color);
+}
+
+.button.iconlabel .label {
+ margin: 3px;
+ margin-left: 8px;
+}
+
+.button.colorpicker .labelwrapper {
+ width: 150px;
+ height: 24px;
+}
+
+.button.colorpicker .labelwrapper .label {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ font-feature-settings: "tnum", "ss01";
+}
+
+.bubble {
+ margin: 0;
+ overflow: visible;
+ left: 50%;
+ top: -24px;
+ transform: translateY(-100%) translateX(-50%);
+}
+
+.bubble .tuitje {
+ bottom: -12px;
+ transform: translate(-50%, 0%) rotate(0deg);
+}
+
+.dialogbox { width: 392px; }
+.dialogbox > .title { margin-bottom: var(--spacing-large); }
+.dialogbox .icon.close {
+ top: 25px;
+ right: 25px;
+ cursor: pointer;
+}
+
+html.dark .dialogbox { background-color: var(--gray-700); }
+
+.searchBar {
+ overflow: hidden;
+}
+
+.searchBar .button {
+ border-radius: 0;
+ padding: 10px;
+}
+
+.searchBar .input {
+ width: calc(100% - 44px);
+ box-sizing: border-box;
+}
+
+.checkbox {
+ cursor: pointer;
+}
+
+.CenteredPageOuter {
+ margin: 0 auto;
+}
+
+.CenteredPageInner {
+ margin: 0 var(--spacing-small);
+ line-height: 0;
+}
+
+.pageTitle {
+ margin-left: var(--spacing-small);
+ margin-top: 32px;
+ margin-bottom: 64px;
+}
+
+.accountAvatar {
+ background-size: cover;
+}
+
+.accountAvatar.round {
+ border-radius: 9999999px;
+}
+
diff --git a/styles/user.css b/styles/user.css
new file mode 100644
index 0000000..d66fa94
--- /dev/null
+++ b/styles/user.css
@@ -0,0 +1,46 @@
+.infosection .inner {
+ height: 64px;
+}
+
+/* editable field status */
+*[contenteditable] {
+ box-shadow: inset 0 0 0 0px var(--foreground);
+}
+*[contenteditable="true"]:focus {
+ box-shadow: inset 0 0 0 2px var(--accent);
+}
+*[contenteditable="true"] {
+ background-color: var(--background);
+ color: var(--text-alt);
+ padding: 8px;
+ box-shadow: inset 0 0 0 2px var(--foreground);
+}
+
+.accountHeader .userInfo {
+ margin-left: var(--spacing-medium);
+ width: calc(100% - 128px - var(--spacing-medium));
+}
+
+.accountHeader .userInfo .username {
+ font-size: 2rem;
+}
+
+.accountHeader .userInfo .status {
+ transition-duration: .3s;
+ margin-top: var(--spacing-small);
+}
+
+.infoModule .iconWrapper {
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.infoModule .labelWrapper {
+ top: calc(24px + var(--spacing-small));
+}
+
+.infoModule .labelWrapper .label {
+ top: 50%;
+ transform: translateY(-50%);
+}
+
diff --git a/styles/utility.css b/styles/utility.css
new file mode 100644
index 0000000..cc358e6
--- /dev/null
+++ b/styles/utility.css
@@ -0,0 +1,102 @@
+.drop-1 { box-shadow: var(--drop-level-1); }
+.drop-2 { box-shadow: var(--drop-level-2); }
+
+.pad-s { padding: var(--spacing-small); }
+.pad-m { padding: var(--spacing-medium); }
+.pad-l { padding: var(--spacing-large); }
+
+.round-l { border-radius: var(--loose-corner); }
+.round-t { border-radius: var(--tight-corner); }
+
+.bg-100 { background-color: var(--gray-100); }
+.bg-200 { background-color: var(--gray-200); }
+.bg-300 { background-color: var(--gray-300); }
+.bg-700 { background-color: var(--gray-700); }
+.bg-800 { background-color: var(--gray-800); }
+.bg-900 { background-color: var(--gray-900); }
+
+.fg-100 { color: var(--gray-100); }
+.fg-900 { color: var(--gray-900); }
+
+.outcome.win { color: var(--disk-a-alt); }
+.outcome.lose { color: var(--disk-b-alt); }
+.outcome.draw { color: var(--gray-600); }
+
+.posabs { position: absolute; }
+.posrel { position: relative; }
+.posfix { position: fixed; }
+
+.t0 { top: 0; }
+.b0 { bottom: 0; }
+.r0 { right: 0; }
+.l0 { left: 0; }
+
+.h0 { left: 0; right: 0; }
+.v0 { top: 0; bottom: 0; }
+
+.a0 { top: 0; bottom: 0; left: 0; right: 0; }
+
+.dispnone { display: none; }
+.dispinbl { display: inline-block; }
+.dispbl { display: block; }
+
+.valigntop { vertical-align: top; }
+.valignsup { vertical-align: super; }
+
+.cpointer { cursor: pointer; }
+.cdefault { cursor: default; }
+
+.center { text-align: center; }
+
+.floatr { float: right; }
+.floatl { float: left; }
+
+.w100m2m { width: calc(100% - var(--spacing-medium)); }
+
+.w100vw { width: 100vw; }
+.h100vh { height: 100vh; }
+
+.fullwidth {
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.subtile {
+ color: var(--gray-600);
+ font-style: italic;
+}
+
+.nosel {
+ user-select: none;
+ font-weight: 600;
+}
+
+.gg-l { grid-gap: var(--spacing-large) !important; }
+.sidebyside {
+ display: grid;
+ grid-gap: var(--spacing-medium);
+ grid-auto-flow: column;
+}
+
+.truncate {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.abscenterh {
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.abscenterv {
+ top: 50%;
+ transform: translateY(-50%);
+}
+
+.abscenter {
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+